您当前的位置: 首页 >  qt

龚建波

暂无认证

  • 6浏览

    0关注

    313博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

SILK SDK + Qt 将QQ/微信的 silk/amr 音频转为 wav 格式

龚建波 发布时间:2020-11-29 20:31:51 ,浏览量:6

0.前言

SILK 编码最早在 Skype 中使用,它在编码效率和质量之间取得了很好的平衡,因此被广泛应用在互联网的音频相关产品中。SILK 的最新版本是 2012 年发布的 SDK 1.0.9,即SILK V3。

腾讯系产品,包括QQ、微信、小程序,在语音相关的实现中,也大量使用到 SILK 编码,不过他在标准的 SILK 文件头加了一个字节的 0x02 ,所以在解码的时候要多判断一次。虽然腾讯的音频文件格式后缀也可能叫 AMR,不过并不是真的 AMR格式,还是 SILK 格式的。

下图是一个微信 AMR 文件的文件头,以 0x02 开头,然后是九个固定字符: #!SILK_V3,接下来就是语音数据。

还要注意的是 SILK 编码的采样频率和码率是自适应的,参见 翻译的文档。

WAV 格式就相对方便点,可以直接存放 PCM 数据,将 SLIK 解码为 PCM 后加一个 WAV 头就成了一个 wav 音频文件( WAV 头的格式参考 WAV格式分析 )。

参考示例:SILK_SDK_SRC_v1.0.9\SILK_SDK_SRC_ARM_v1.0.9\test\Decoder.c

参考博客(转载):https://blog.csdn.net/weixin_34292924/article/details/87987436

参考博客:https://www.cnblogs.com/protosec/p/11673358.html

参考博客:https://blog.csdn.net/wanggp_2007/article/details/5536818

参考博客(翻译):https://blog.csdn.net/zhaosipei/article/details/7467810

别人fork的SDK源码:https://gitee.com/alvis/SILK_SDK_SRC

别人做的decoder:https://github.com/kn007/silk-v3-decoder

1.实现

下载好 SILK SDK 后,可以直接用 VS 打开工程文件进行编译为静态库,然后将 interface 文件夹和库文件导入到我们的应用工程中。也可以自己建一个 DLL 工程把那些原文件 copy 过去,然后导出 SKP_Silk_SDK_API.h 文件中的函数接口,生成动态或者静态库。

借助 SLIK 库,解码主要有两步,初始化解码器+循环解码为 PCM 数据。

转为 WAV 也两步,生成文件头+写入 PCM 数据。

代码链接(github):https://github.com/gongjianbo/MyTestCode/tree/master/Qt/SilkToWav

工程链接(CSDN):https://download.csdn.net/download/gongjianbo1992/13206416

主要代码:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

/**
 * @brief wav文件头结构体
 * @author 龚建波
 * @date 2020-11-12
 * @details
 * wav头是不定长格式,不过这里用的比较简单的格式
 * (数值以小端存储,不过pc一般是小端存储,暂不特殊处理)
 * 参照:https://www.cnblogs.com/ranson7zop/p/7657874.html
 * 参照:https://www.cnblogs.com/Ph-one/p/6839892.html
 */
struct EasyWavHead
{
    char riffFlag[4]; //文档标识,大写"RIFF"
    //从下一个字段首地址开始到文件末尾的总字节数。
    //该字段的数值加 8 为当前文件的实际长度。
    unsigned int riffSize; //数据长度
    char waveFlag[4]; //文件格式标识,大写"WAVE"
    char fmtFlag[4]; //格式块标识,小写"fmt "
    unsigned int fmtSize; //格式块长度,可以是 16、 18 、20、40 等
    unsigned short compressionCode; //编码格式代码,1为pcm
    unsigned short numChannels; //通道个数
    unsigned int sampleRate; //采样频率
    //该数值为:声道数×采样频率×每样本的数据位数/8。
    //播放软件利用此值可以估计缓冲区的大小。
    unsigned int bytesPerSecond; //码率(数据传输速率)
    //采样帧大小。该数值为:声道数×位数/8。
    //播放软件需要一次处理多个该值大小的字节数据,用该数值调整缓冲区。
    unsigned short blockAlign; //数据块对其单位
    //存储每个采样值所用的二进制数位数。常见的位数有 4、8、12、16、24、32
    unsigned short bitsPerSample; //采样位数(采样深度)
    char dataFlag[4]; //表示数据开头,小写"data"
    unsigned int dataSize; //数据部分的长度
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void paintEvent(QPaintEvent *event) override;

    //silk解码为pcm
    void decodeSilk(const QString &filepath);

    //生成wav(pcm)文件头信息
    //sampleRate: 采样频率
    //dataSize: pcm数据字节长度
    //return EasyWavHead: wav头
    static EasyWavHead createWavHead(int sampleRate,unsigned int dataSize);

private:
    Ui::MainWindow *ui;

    QByteArray pcmData;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include 
#include 
#include 
#include 
#include 

#include "SKP_Silk_SDK_API.h"

#define MAX_BYTES_PER_FRAME     1024
#define MAX_INPUT_FRAMES        5
#define MAX_FRAME_LENGTH        480
#define FRAME_LENGTH_MS         20
#define MAX_API_FS_KHZ          48

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

    ui->lineEdit->setText(qApp->applicationDirPath()+"/weixin.amr");

    //选择文件
    connect(ui->btnFile,&QPushButton::clicked,[=]{
        const QString filepath=QFileDialog::getOpenFileName(this);
        if(!filepath.isEmpty())
            ui->lineEdit->setText(filepath);
    });

    //解码
    connect(ui->btnDecode,&QPushButton::clicked,[=]{
        const QString filepath=ui->lineEdit->text();
        if(filepath.isEmpty())
            return;
        qDebug()            
关注
打赏
1655829268
查看更多评论
0.0549s