在这篇文章麦克风采集生成波形图描述了如何使用Qml中的Chart组件来绘制波形图,但是有时候我们需要绘制一些额外的信息,比如横轴和纵轴也要能够自定义,这个时候在qml-chart中就比较难定制了,我们可以通过继承Qt中的QQuickPaintedItem实现重绘事件,再将继承类注册到qml中,这样我们就能够在C++实现将录音的数据绘制出来

  1. 首先我们需要继承QQuickPaintedItem这个类,顾名思义,这个类是可以做绘制的。
    实现绘制主要在重载函数 void paint(QPainter *painter) override;
#ifndef AUDIOWAVEITEM_H
#define AUDIOWAVEITEM_H#include <QIODevice>
#include <QAudioInput>
#include <QQuickPaintedItem>#ifndef MINSHORT
#define MINSHORT    0x8000
#define MAXSHORT    0x7fff
#endiftypedef struct ScaleSamplePoint {short pointV;short maxV;short miniV;bool maxAtRight;long long where;} SCALE_SAMPLE_POINT;class AudioDataSource :  public QIODevice
{Q_OBJECT
public:explicit AudioDataSource(QObject *parent = nullptr);protected:qint64 readData(char * data, qint64 maxSize);qint64 writeData(const char * data, qint64 maxSize);
signals:void sigUpdateAudioData(QByteArray audioData);
};
class AudioWaveItem : public QQuickPaintedItem
{Q_OBJECT
public:AudioWaveItem(QQuickItem *parent = nullptr);Q_INVOKABLE void startRecord();Q_INVOKABLE void stopRecord();/*** 整个item的区域* @brief rect* @return*/QRectF rect() const;/*** 主绘图区域,不包括margin* @brief mainRect* @return*/QRectF mainRect() const;
protected:void paint(QPainter *painter) override;
signals:void sigStopRecordData();
public slots:void updateAudioData(QByteArray audioData);
private:std::shared_ptr<QAudioInput>        m_audioInput;std::shared_ptr<AudioDataSource>    m_audioDataSource;QByteArray                          m_audioData;bool                                m_recordMode;std::shared_ptr<SCALE_SAMPLE_POINT> m_shownPoints;bool m_enableXAxis;bool m_enableYAxis;QMarginsF m_margins;
private:void drawGrid(QPainter *painter, int maxV, int minV,int sampleShown, bool isRecordMode/* = false*/);
};
#endif // AUDIOWAVEITEM_H
  1. 在这个类里,有m_margins是来定义一个偏移区域来绘制纵轴,绘制横轴和纵轴主要定义在__void drawGrid()__函数里
  2. MAXSHORT__和__MINSHORT__定义short的最大数,这个主要是采集音频的时候采集精度设置16bit位宽,取数据的时候需要映射到这个范围里,具体可以看__void paint()
  3. AudioDataSource 继承__QIODevice__,主要是接收QAudioInput的数据,也即录音数据
  4. m_shownPoints 是需要绘制的控制点,对应绘制区域的每个像素
#include "AudioWaveItem.h"
#include <QPainter>
#include <functional>#include <QtDebug>
#include <QPainter>
#include <QCursor>qreal calcRatioFromValue3(qreal zeroPoint, short v, int maxV = MAXSHORT, int minV = short(MINSHORT)) {qreal n = 0.0;//maxV和minV不做0判断,浪费计算if (v >= 0) {n = zeroPoint - zeroPoint * (qreal(v) / qreal(maxV));} else {n = zeroPoint + zeroPoint * (qreal(v) / qreal(minV));}return n;
}AudioDataSource::AudioDataSource( QObject *parent) :QIODevice(parent)
{}qint64 AudioDataSource::readData(char * data, qint64 maxSize)
{Q_UNUSED(data)Q_UNUSED(maxSize)return -1;
}qint64 AudioDataSource::writeData(const char * data, qint64 maxSize)
{QByteArray audioData(data, maxSize);emit sigUpdateAudioData(audioData);return maxSize;
}AudioWaveItem::AudioWaveItem(QQuickItem *parent): QQuickPaintedItem(parent)
{setFlag(ItemAcceptsInputMethod, true);setAcceptedMouseButtons(Qt::AllButtons);setAcceptHoverEvents(true);m_margins = QMarginsF(50, 0, 5, 0);m_enableXAxis = true;m_enableYAxis = true;m_recordMode = true;m_audioDataSource.reset(new AudioDataSource());m_audioDataSource->open(QIODevice::WriteOnly);connect(m_audioDataSource.get(), &AudioDataSource::sigUpdateAudioData,this, &AudioWaveItem::updateAudioData);m_shownPoints.reset(new SCALE_SAMPLE_POINT[10000]);memset(m_shownPoints.get(), 0, 10000);}void AudioWaveItem::paint(QPainter *painter)
{int maxV = short(MAXSHORT);int minV = short(MINSHORT);drawGrid(painter, maxV, minV, abs(this->width()), true);if(m_audioData.size() == 0){return;}QPen pen = painter->pen();auto midHeight = this->mainRect().height() / 2;pen.setColor(QColor(0, 255, 0));pen.setWidth(1.0);painter->setPen(pen);int x = 0;QPointF lastP;for(int i = 0; i < this->mainRect().width() - 1; i++){short _maxV = this->m_shownPoints.get()[i].maxV;short _miniV = this->m_shownPoints.get()[i].miniV;qreal _maxH = 0.0;qreal _miniH = 0.0;{_maxH = calcRatioFromValue3(midHeight, _maxV);}{_miniH = calcRatioFromValue3(midHeight, _miniV);}x = m_margins.left() + (qreal)(i);QPointF p0(x, floor(_maxH));QPointF p1(x, floor(_miniH));if (i > 0) {QPointF p;if (this->m_shownPoints.get()[i].maxAtRight) {p = p1;}  else {p = p0;}if (p.y() != midHeight || lastP.y() != midHeight)painter->drawLine(lastP, p);}if (this->m_shownPoints.get()[i].maxAtRight) {lastP = p0;}  else {lastP = p1;}painter->drawLine(p0, p1);}
}
void AudioWaveItem::startRecord()
{QAudioFormat formatAudio;formatAudio.setSampleRate(441000);formatAudio.setChannelCount(2);formatAudio.setSampleSize(16);formatAudio.setCodec("audio/pcm");formatAudio.setByteOrder(QAudioFormat::LittleEndian);formatAudio.setSampleType(QAudioFormat::UnSignedInt);QAudioDeviceInfo inputDevices = QAudioDeviceInfo::defaultInputDevice();m_audioInput.reset(new QAudioInput(inputDevices, formatAudio));m_audioInput->start(m_audioDataSource.get());m_recordMode = true;}void AudioWaveItem::updateAudioData(QByteArray audioData)
{m_audioData.append(audioData);if(m_audioData.size() / 2 < this->mainRect().width()){return;}int dx = 0;int maxSize = 180000;short _maxV = -32768;short _miniV = 32767;bool _maxAtRight = false;short* sampleData = (short*)m_audioData.data();if(m_audioData.size() / 2 < maxSize){dx = m_audioData.size() / 2 / mainRect().width();int idx = 0;for(int i = 0; i < this->mainRect().width() - 1; i++){for (int n = i * dx; n < (i + 1) * dx; n++) {short value = sampleData[n];if (value >= _maxV) {_maxAtRight = true;_maxV = value;}if (value <= _miniV) {_maxAtRight = false;_miniV = value;}}m_shownPoints.get()[idx].maxAtRight = _maxAtRight;m_shownPoints.get()[idx].maxV = _maxV;m_shownPoints.get()[idx].miniV = _miniV;m_shownPoints.get()[idx].pointV = sampleData[i * dx];idx++;_maxAtRight = false;_maxV = -32768;_miniV = 32767;//qDebug() << "updatedata" << "0";}}else{dx = maxSize / mainRect().width();int idx = 0;int start = m_audioData.size() / 2 - maxSize;for(int i = 0; i < this->mainRect().width() - 1; i++){for (int n = start + i * dx; n < start + (i + 1) * dx; n++) {short value = sampleData[n];if (value >= _maxV) {_maxAtRight = true;_maxV = value;}if (value <= _miniV) {_maxAtRight = false;_miniV = value;}}m_shownPoints.get()[idx].maxAtRight = _maxAtRight;m_shownPoints.get()[idx].maxV = _maxV;m_shownPoints.get()[idx].miniV = _miniV;//m_shownPoints.get()[idx].pointV = sampleData[i * dx];idx++;_maxAtRight = false;_maxV = -32768;_miniV = 32767;//qDebug() << "updatedata" << "0";}}update();
}void AudioWaveItem::stopRecord()
{m_audioInput->stop();this->update();emit sigStopRecordData();
}void AudioWaveItem::drawGrid(QPainter *painter, int maxV, int minV,int sampleShown, bool isRecordMode/* = false*/)
{auto pen = painter->pen();painter->save();painter->setPen(Qt::NoPen);painter->setBrush(QColor(34, 34, 34));painter->drawRect(this->rect());painter->restore();painter->save();painter->setPen(Qt::NoPen);painter->setBrush(QColor(0, 0, 0));painter->drawRect(this->mainRect());painter->restore();qreal realWidth = this->mainRect().width();qreal height = this->height();qreal y0 = height / 2;pen.setWidth(1);qreal dx = realWidth / (qreal)sampleShown;qreal x = 0.0;QPointF lastP;if (m_enableYAxis) {//绘制纵坐标pen.setColor(QColor(200, 200, 200));pen.setWidth(1.0);painter->setPen(pen);qreal fdy = (float)y0 / abs(minV);static std::function<void (QPainter *, int, qreal, qreal, qreal, qreal)> _drawTextFunc =[&](QPainter *painter, int value, qreal _x, qreal _y, qreal top, qreal bottom) {QString text = QString::number(value);auto m_yParmas = 0;if(m_yParmas == 0)//采样值{if (text.size() > 3) {text = text.mid(0, text.size() - 3) + "k";}}else if(m_yParmas == 1){//标准值if (text.size() > 3) {text = text.mid(0, text.size() - 3);text = QString::number(text.toFloat() / 5.0f * 0.166f, 'f', 2);}}else if(m_yParmas == 2){//百分比if (text.size() > 3) {text = text.mid(0, text.size() - 3);text = QString::number(text.toFloat() / 5.0f * 16.6f, 'f', 0) + "%";}}qreal _w = painter->fontMetrics().horizontalAdvance(text);qreal _h = painter->fontMetrics().height();QRectF textRect(0, 0, _w, _h);textRect.moveCenter(QPointF(_x - _w / 2 - 3, _y));if (textRect.top() < top) {return;} else if (textRect.bottom() > bottom) {return;}QTextOption to;to.setAlignment(Qt::AlignHCenter | Qt::AlignRight);painter->drawText(textRect, text, to);};QString digitalStr = QString::number(int(abs(minV) / 6));int yAxisSkip = digitalStr.mid(0, 1).toUInt() * pow(10, digitalStr.size() - 1);if (yAxisSkip == 0) {yAxisSkip = 1;}int count = abs(minV) / yAxisSkip + 1;for (int i = 0; i < count; i++) {int value = yAxisSkip * i;qreal _y = y0 - yAxisSkip * fdy * i;qreal _x0 = this->mainRect().x() - 6;qreal _x1 = this->mainRect().x() - 1;painter->drawLine(QPointF(_x0, _y), QPointF(_x1, _y));_drawTextFunc(painter, value, _x0, _y, this->mainRect().top(), this->mainRect().bottom());if (i == 0)continue;if(m_enableYAxis){if (sampleShown > 0) {painter->save();pen.setWidth(1);pen.setColor(QColor(0, 33, 0));painter->setPen(pen);painter->drawLine(QPointF(this->mainRect().x(), _y), QPointF(this->mainRect().x() + realWidth - 1, _y));painter->drawLine(QPointF(this->mainRect().x(), this->mainRect().height() / 2),QPointF(this->mainRect().x() + realWidth - 1, this->mainRect().height() / 2));painter->restore();}}}for (int i = 1; i < count; i++) {int value = -yAxisSkip * i;float _y = y0 + yAxisSkip * fdy * i;qreal _x0 = this->mainRect().x() - 6;qreal _x1 = this->mainRect().x() - 1;painter->drawLine(QPointF(_x0, _y), QPointF(_x1, _y));_drawTextFunc(painter, value, _x0, _y, this->mainRect().top(), this->mainRect().bottom());if (m_enableYAxis) {if (sampleShown > 0) {painter->save();pen.setWidth(1);pen.setColor(QColor(0, 33, 0));painter->setPen(pen);painter->drawLine(QPointF(this->mainRect().x(), _y), QPointF(this->mainRect().x() + realWidth - 1, _y));painter->restore();}}}}if (m_enableXAxis) {//绘制网格painter->save();pen.setWidth(1);pen.setColor(QColor(0, 33, 0));painter->setPen(pen);for (int var = 0; var < 8; ++var) {qreal x = this->mainRect().width() / (qreal)8 * var;painter->drawLine(QLineF(x, this->mainRect().top(), x, this->mainRect().bottom()));}painter->restore();}
}QRectF AudioWaveItem::rect() const
{return boundingRect();
}QRectF AudioWaveItem::mainRect() const
{QRectF mainRect = this->rect().adjusted(m_margins.left(), m_margins.top(), -1 * m_margins.right(), -1 * m_margins.bottom());return mainRect;
}
  1. 在__void updateAudioData()__,主要是将录音数据可视化,主要是将一段区间内的数据按绘制区间分块,取该区间的最大值(对应波形图上半区)或最小值(对应波形图下半区),比如在采样频率为16k,采样位宽16bit,双通道的音频数据来说,一秒钟的数据就为32k个采样点,将这么多的采样点分块(比如绘制区域为500个像素点——通过mainRect().width可以获取),那么就取每32k/500=640个采样点去计算极值;当然也可以每个像素点对应一个采样点,但是这样有很大几率绘制出来是一个曲线
  2. __void drawGrid()__这一块没什么好讲的,主要是将绘制区域分块,然后用__painter.drawline()__和__painter.drawText()__绘制出来
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <AudioWaveItem.h>
int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);qmlRegisterType<AudioWaveItem>("VoiceRecord", 1, 0, "AudioWaveItem");QQmlApplicationEngine engine;const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);return app.exec();
}

在main函数需要主要的是需要将AudioWaveItem注册到qml中
qmlRegisterType(“VoiceRecord”, 1, 0, “AudioWaveItem”);
同时在qml中引用
import VoiceRecord 1.0

效果截屏

工程下载地址:
Qt+qml 麦克风采集生成波形图(二)—— 工程代码下载

QtQuick 麦克风采集生成波形图(二)相关推荐

  1. 如何使用c语言获取麦克风信息,[C#] 如何获取麦克风采集的音频信息 和 如何根据波形播放声音。...

    你好: 我对这方面也不懂,但是我觉得如果想分析音频数据的话首先要知道麦克风采集到的音频是什么格式的,然后需要了解这种格式的音频数据的二进制流的编码规范,比如wav格式的音频,参考一下这篇博客, wav ...

  2. EFQRCode:自动生成花式二维码

    原文链接:https://github.com/EyreFree/EFQRCode EFQRCode:自动生成花式二维码.# 为开源点赞# -- 由SwiftLanguage分享 EFQRCode i ...

  3. 程序员的乐趣,生成自定义二维码,5 行 Python 代码就搞定

    选自 | towardsdatascience 作者 | Arindom Bhattacharjee 转自 | 机器之心 参与 | 杜伟.小舟 随处可见的二维码是怎么生成的?自己做一个试试吧. 随着互 ...

  4. 支付宝支付 第六集:生成支付二维码

    支付宝支付 第六集:生成支付二维码 一.资源 支付宝沙箱显示APP-ID错误:我的原因是支付宝网关地址写错了 支付宝沙箱环境报 invalid-app-id 错误原因: 无效的AppID参数 错误 支 ...

  5. Android应用--QR的生成(二维码)

    二维码的定义: 二维码 (2-dimensional bar code),是用某种特定的几何图形按一定规律在平面(二维方向上) 分布的黑白相间的图形记录数据符号信息的. 在许多种类的二维条码中,常用的 ...

  6. python小工具myqr生成动态二维码

    python小工具myqr生成动态二维码 (一)安装 (二)使用 (一)安装 命令: pip install myqr 安装完成后,就可以在命令行中输入 myqr 查看下使用帮助: myqr --he ...

  7. 分享几个在线生成网址二维码的API接口

    现在很多大网站都有这样的一个功能,使用手机扫描一下网页上的二维码便可快速在手机上访问网站.想要实现这样的功能其实很简单,下面麦布分享几个在线生成网址二维码的API接口.都是采用http协议接口,无需下 ...

  8. qrcode方法生成的二维码安卓手机长按不识别

    qrcode生成的二维码,在苹果等手机上长按可识别,在华为手机上长按没有反应,截图保存下来长按又可以识别. #问题原因 浏览器兼容问题 qrcode在页面生成二维码时,会生成一个canvas标签和一个 ...

  9. python生成动态二维码实例_python生成动态个性二维码(示例代码)

    1 安装工具 2 生成普通二维码 3 带图片的二维码 4 动态 GIF 二维码 5 在Python程序中使用 一.安装 首先在python环境下运行, 打开cmd进入python27 进入script ...

最新文章

  1. C++函数指针解引用
  2. python进程线程处理模块_python程序中的线程操作 concurrent模块使用详解
  3. jmeter---linux安装运行
  4. stcisp一直检测单片机_三种方法对比:STC51单片机实现免冷启动
  5. python安装pyinstaller库_python pyinstaller安装
  6. C#连接sqlServer数据库详解
  7. .Net Core迁移到MSBuild平台
  8. 最简单的java单例
  9. Qt文档阅读笔记-QPropertyAnimation官方解析及实例
  10. 嵌入式视频处理基础(四)
  11. Hibernate简单的保存操作
  12. 人工神经网络简介和单层网络实现AND运算--AForge.NET框架的使用(五)
  13. MindSpore,易用性提升的思考与实践
  14. 怎么说话比说什么更重要
  15. GCD,快速GCD,扩展GCD
  16. SpringBoot——安全管理(一)
  17. 制作 OS X El Capitan 启动盘
  18. Python3 爬取豆瓣电影信息
  19. 怎么用计算机玩绝地求生,8g内存玩绝地求生卡顿怎么办?绝地求生大逃杀内存设置优化图文教程...
  20. 手把手教你实现人脸识别,有手就行

热门文章

  1. 用c 语言如何实现鼠标画图,C在控制台上实现鼠标画图功能
  2. 王者荣耀s14服务器维护时间,王者荣耀S14具体开启时间 王者荣耀S14赛季什么时候开...
  3. pcep协议什么意思_协议是什么意思??
  4. 史上最全Java工程师面试题汇总,全会月薪至少3W
  5. 【HCIP】OSPF协议的五种报文格式
  6. 2019-1-24【训练日记】
  7. 什么叫做“微信式”的恋爱?
  8. 程序员需要更多的数学知识
  9. dtu MySQL_dtu数据采集传输设备
  10. dism失败 ox800f0818_win10无法安装 .NET framework 3.5 错误代码 0x8e5e03fa - Microsoft Community...