Qt显示wav波形图

1.参考资料:

https://www.docin.com/p-1263172990.html

https://wenku.baidu.com/view/738ea046fd4ffe4733687e21af45b307e971f96f.html

2.涉及QT内容:

2.1 使用QMediaPlayer来播放音频。

    
    //在.pro文件中加入  QT       += multimedia
    QMediaPlayer player;
    QMediaPlaylist playlist;
    playlist.addMedia(QUrl("xx.wav"));
    player.setPlaylist(&playlist);
    player.play();
    player.pause();
    player.stop();

2.2 使用QPainter,QPixmap绘制波形图。

    QPixmap pixmap(100, 20);
    pixmap.fill(Qt::black);    
    QPainter painter(&pixamp);
    painter.drawLine(QLine());

3.波形图绘制原理。

注意:本文章只实现声道数为1,2,采样位数为8,16的wav非压缩(PCM)音频;

①根据绘制区域得到宽高,再得出绘制波形图的每条竖线需要N个采样点,然后N个连续采样点中取出最大值和最小值,然后比例计算竖线;

②获取采样位数为8时,数据大小范围为0~255,获取采样位数为16时,数据大小范围为-32768~32767.

关键源码:

    if (fileWav.endsWith(".wav", Qt::CaseInsensitive) && QFile(fileWav).exists())
    {
        if (m_qUnplay.width() == 0)
        {
            return;
        }

        m_qUnplay.fill(Qt::black);
        QPainter painter1(&m_qUnplay);
        painter1.setPen(QColor(75, 243, 167));

        m_qPlaying.fill(QColor(98, 108, 123));
        QPainter painter2(&m_qPlaying);
        painter2.setPen(QColor(75, 243, 167));

        FILE* fp;
        if ((fp = fopen(fileWav.toLocal8Bit().toStdString().c_str(), "rb")) != nullptr)
        {
            struct WavHead  //wave文件头格式
            {
                char chunkID[4];            //文档标识 固定值"RIFF"
                qint32 chunkSize;           //文件数据长度
                char format[4];             //文件格式类型 固定值为"WAVE"
                qint32 subChunk1ID;         //格式块标识   固定值为"fmt"
                qint32 subChunk1Size;       //格式块长度 取决于编码格式
                qint16 audioFormat;         //编码格式代码 (PCM/非压缩格式)
                qint16 numChannels;         //声道个数
                qint32 sampleRate;          //采样频率
                qint32 byteRate;            //传输速率
                qint16 blockRate;           //数据块对齐单位
                qint16 bitsPerSample;       //采样位数
                char subChunk2ID[4];
                qint32 subChunk2Size;
            }stuWavHead;

            fread(&stuWavHead, sizeof(stuWavHead), 1, fp);

            //只支持声道数为1,2的非压缩格式(PCM)
            if ((stuWavHead.numChannels != 1 && stuWavHead.numChannels != 2)
                    || stuWavHead.audioFormat != 1)
            {
                return ;
            }

            int dataSize = stuWavHead.chunkSize - sizeof(stuWavHead) - 8;
            //样本总数
            int sampleCount = dataSize / NO_ZERO(stuWavHead.bitsPerSample / 8);
            //只去左声道样本
            sampleCount = sampleCount / stuWavHead.numChannels;
            //每条竖线的样本数
            int linePerSample = sampleCount / m_qPlaying.width();
            int minY = 0;
            int maxY = 0;
            if (stuWavHead.bitsPerSample == 8)
            {
                quint8 data;
                quint8 min = 255;
                quint8 max = 0;
                int x = 0;
                for (int i = 1; i <= sampleCount; i++)
                {
                    fread(&data, 1, 1, fp);
                    if (stuWavHead.numChannels == 2)
                    {
                        fseek(fp, 1, SEEK_CUR);
                    }
                    if (min > data)
                    {
                        min = data;
                    }

                    if (max < data)
                    {
                        max = data;
                    }

                    if (i % linePerSample == 0)
                    {
                        maxY = m_qPlaying.height() - max * m_qPlaying.height() / 255;
                        minY = m_qPlaying.height() - min * m_qPlaying.height() / 255;
                        painter1.drawLine(QPoint(x, minY), QPoint(x, maxY));
                        painter2.drawLine(QPoint(x, minY), QPoint(x, maxY));
                        qDebug() << "min:" << min;
                        qDebug() << "max:" << max;
                        min = 255;
                        max = 0;
                        x++;
                    }
                }
            }
            else if (stuWavHead.bitsPerSample == 16)
            {
                qint16 data;
                qint16 min = 32767;
                qint16 max = min + 1;
                int x = 0;
                for (int i = 1; i <= sampleCount; i++)
                {
                    fread(&data, 2, 1, fp);
                    if (stuWavHead.numChannels == 2)
                    {
                        fseek(fp, 2, SEEK_CUR);
                    }
                    if (min > data)
                    {
                        min = data;
                    }

                    if (max < data)
                    {
                        max = data;
                    }

                    if (i % linePerSample == 0)
                    {
                        maxY = m_qPlaying.height() / 2 - max * m_qPlaying.height() / 65535;
                        minY = m_qPlaying.height() / 2 - min * m_qPlaying.height() / 65535;
                        painter1.drawLine(QPoint(x, minY), QPoint(x, maxY));
                        painter2.drawLine(QPoint(x, minY), QPoint(x, maxY));
                        min = 32767;
                        max = min + 1;
                        x++;
                    }
                }
            }
        }
    }

效果:

 

  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值