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()
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?