Qt_模仿siri语音动画 全平台语音动画




1.效果展示

[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.dnimg.cn/20201120XjRw1014169.png#pic_cee28)(https://imgblog.csdnimg.cn/202011201014169.png#pic_center)]



2.简述

模仿一个下面的语音动画,看起来肯定得用贝塞尔曲线,先用Ps钢笔勾出来这个轮廓,研究一下变大变小是什么参数在动。
在这里插入图片描述
一截贝塞尔曲线有三个参数,两个手柄和一个锚点,这种形状的规律是手柄1的x坐标是固定的,根据声音值的大小,手柄1的y坐标是上下浮动的,我们把数值定为0~100的浮动。而锚点和手柄2是相同的坐标,这样就很好计算了。只有一个变量,就是手柄1跟着声音大小变就行了。
在这里插入图片描述
但是siri每个波的周期还不是一样的,那我们就给一个随机的X偏移,这样看起来就灵动些。
在这里插入图片描述

3.代码

工程下载链接https://mp.csdn.net/console/upDetailed

测试用例

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

    m_pTimer = new QTimer(this);
    connect(m_pTimer,&QTimer::timeout,[=]{

        qsrand(QTime::currentTime().msec());
        QVector<int> vecData;
        vecData.append(2+qrand() % 8);
        for(int i = 0; i < 3; i++){

            vecData.append(qrand() % 80); //0~100之间都行
        }
        vecData.append(2+qrand() % 8);

        ui->pSonicAnmation->setData(vecData); //模拟声波数据 值在0~100的范围
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{

}

void MainWindow::on_pushButton_clicked(bool checked)
{
    if(checked){
        ui->pushButton->setText("停止");
        m_pTimer->start(200);
    }else{
        ui->pushButton->setText("开始");
        m_pTimer->stop();
    }
}

CSonicAnmation.cpp

#include "CSonicAnmation.h"
#include <QDebug>
#include <QTime>

CSonicAnmation::CSonicAnmation(QWidget *parent) : QWidget(parent)
{
    qsrand(QTime::currentTime().msec()); //随机种子 为随机颜色做准备
}

void CSonicAnmation::setData(QVector<int> &vecData)
{
    if(m_vecSonic.size() > 2){
        m_vecSonic.takeLast();
    }
    m_vecSonic.append(vecData);

    update();
}

void CSonicAnmation::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true); //开启抗锯齿


    //描边画笔
    painter.setPen(QPen(QBrush(),0));

    //00坐标变换到左侧中间位置
    painter.translate(0,height()/2);
    //    painter.drawPoint(0,0); 画坐标原点

    foreach (QVector<int> vecData, m_vecSonic) {
        painter.setBrush(QBrush(randLineGradient())); //随机渐变填充
        painter.drawPath(calcCublic(vecData,painter));
    }
}

QPainterPath CSonicAnmation::calcCublic(QVector<int> vecData, QPainter &painter)
{
    Q_UNUSED(painter) //调试画锚点坐标用到

    m_nSpan = this->width() / vecData.size(); //计算每一节的宽度
    //计算手柄和锚点坐标
    QVector<StCubic> vecCubic;
    vecCubic.resize(vecData.size()*2);

    for (int i = 0; i < vecData.count(); i++) {
        //手柄1坐标
        QPointF ptHandle(m_nSpan * (i+1) - m_nSpan /2,((double)vecData.at(i)/100.00) * this->height());//第一个手柄  另一个手柄等于锚点
        QPointF ptAnchor(m_nSpan * (i+1),0); //锚点
        vecCubic[i] = StCubic(ptHandle,ptAnchor,ptAnchor);
        //        qDebug()<<"手柄D:"<<ptHandle<<"锚点D:"<<i<<ptAnchor;

        //锚点坐标
        ptHandle.setY(-ptHandle.y());
        ptAnchor.setX(ptAnchor.x() - m_nSpan);
        vecCubic[vecCubic.size() -1 - i] = StCubic(ptHandle,ptAnchor,ptAnchor);//手柄2坐标和锚点相同
        //        qDebug()<<"手柄U:"<<ptHandle<<"锚点U:"<<vecCubic.size() -1 - i<<ptAnchor;
    }

    randX(vecCubic);


    //勾勒贝塞尔曲线
    QPainterPath pathSonic(QPoint(0,0));
    for(int i = 0; i < vecCubic.size(); i++) {
        StCubic stCubic = vecCubic.at(i);
        pathSonic.cubicTo(stCubic.ptfHandle1,stCubic.ptfAnchor,stCubic.ptfAnchor);
        //调试显示手柄和锚点 不要删除
        //        painter.drawText(stCubic.ptfHandle1,QString("手柄%1").arg(i));
        //        painter.drawEllipse(stCubic.ptfHandle1,3,3);

        //        QPointF ptf(stCubic.ptfAnchor);
        //        ptf.setY(i < m_vecCubic.size()/2 ? -10 : 10);
        //        painter.drawText(ptf,QString("锚点 %1").arg(i));
        //        painter.drawRect(QRect(stCubic.ptfAnchor.toPoint(),QSize(3,3)));
    }

    return pathSonic;
}

void CSonicAnmation::randX(QVector<StCubic> &vecCubic)
{
    qsrand(QTime::currentTime().msec());

    for(int i = 0; i < vecCubic.size()/2 - 1; i++){
        //让锚点x坐标保持一定的随机性 看起来不那么机械
        int nOffsetX = (qrand() % m_nSpan) * 0.4; //X坐标随机偏移40%  可自由设置
        nOffsetX = nOffsetX & 1 ? -nOffsetX : nOffsetX;
        qDebug()<<"随机数:"<<nOffsetX;

        //设置对应的锚点随机偏移
        int nIndexDown = vecCubic.size() - (i+2);
        int nIndexUp = i;
        vecCubic[nIndexDown].ptfAnchor.setX( vecCubic[nIndexDown].ptfAnchor.x() + nOffsetX);
        vecCubic[nIndexUp].ptfAnchor.setX(vecCubic[nIndexUp].ptfAnchor.x() + nOffsetX);

        qDebug()<<"锚点Down:"<<nIndexDown<<"="<<"锚点Up:"<<nIndexUp;
    }

}

QColor CSonicAnmation::randColor()
{
    //    int nS = qrand() % 255; //随机透明度
    //    int nR = qrand() % 255; //随机红色
    //    int nG = qrand() % 255; //随机绿色
    //    int nB = qrand() & 255; //随机蓝色

    //    return QColor(nS,nR,nG,nB);

    //随机浅色
    QColor colorRand;
    colorRand.setHsv(qrand() % 360,255,255,150); //随机色相 固定饱和度、明度、透明度  保持颜色为浅色

    return colorRand;
}

QLinearGradient CSonicAnmation::randLineGradient()
{
    QLinearGradient linearGradient(0, 0, this->width(), 0);//初始化,设置开始和结束点
    linearGradient.setColorAt(0.0, randColor());
    linearGradient.setColorAt(0.5, randColor());
    linearGradient.setColorAt(1.0, randColor());
    return linearGradient;
}

CSonicAnmation.h

#ifndef CSONICANMATION_H
#define CSONICANMATION_H

#include <QWidget>
#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
#include <QQueue>

struct StCubic{
    StCubic(){}
    StCubic(QPointF &ptfHandle1,QPointF &ptfAnchor,QPointF &ptfHandle2)
    {
        this->ptfHandle1 = ptfHandle1;
        this->ptfAnchor = ptfAnchor;
        this->ptfHandle2 = ptfHandle2;
    }

    QPointF ptfHandle1;
    QPointF ptfAnchor;
    QPointF ptfHandle2; //暂时没用到
};

class CSonicAnmation : public QWidget
{
    Q_OBJECT
public:
    explicit CSonicAnmation(QWidget *parent = 0);

    void setData(QVector<int> &vecData); //设置声波数据 建议五个数即可 数越多波浪越多
protected:
    virtual void paintEvent(QPaintEvent *event);

private:
    QPainterPath calcCublic(QVector<int> vecData,QPainter &painter);
    void randX(QVector<StCubic> &vecCubic); //X坐标随机处理
    QColor randColor();
    QLinearGradient randLineGradient();
private:

    QVector<QVector<int>> m_vecSonic; //存储几行声波数据 目前3层
    int m_nSpan; //每个波所占的宽度

#endif // CSONICANMATION_H

Qt框架下模仿iOS语音波形图,可以使用Qt Multimedia模块和Qt Quick绘图功能来实现。 首先,我们需要使用Qt Multimedia模块录制音频,并获取音频的波形数据。通过QAudioInput类可以实现音频录制,并使用QAudioFormat设置音频格式。在录制过程中,可以通过QBuffer类收集音频数据,并使用QAudioBuffer将音频数据转换为波形数据。 接下来,我们可以将获取的波形数据显示在Qt Quick界面上的图形组件中。可以创建一个自定义的波形图形组件,继承自QQuickPaintedItem,并实现paint方法来绘制波形图形。在paint方法中,根据波形数据使用QPainter进行绘制,并根据音频的时间轴来确定波形的位置和形状。可以使用不同的颜色和样式来显示波形,以逼真地模仿iOS的语音波形图。 除了绘制波形图形,我们还可以为波形图形组件添加一些交互功能,例如拖动、缩放等。可以通过重写鼠标事件和触摸事件来实现这些功能,例如根据鼠标或手指的位置来确定当前选择的波形片段,并提供相应的操作。 最后,我们将自定义的波形图形组件嵌入到Qt Quick的界面中,通过QML语言进行布局和样式的调整。可以根据需要添加其他的界面元素,例如播放按钮、录制按钮等。 通过以上步骤,我们可以在Qt实现一个模仿iOS语音波形图的界面。这样可以让用户在Qt应用程序中更加直观和便捷地查看和编辑音频波形数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是唐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值