zoukankan      html  css  js  c++  java
  • SILK编码语音转WAV格式

      SILK音频编码格式介绍、WAV文件封装格式介绍以及SILK音频编码到WAV的转换


    在语音相关的协议还原中,经常会遇到语音编码的问题,通常语音编码的数据无法直接展示,需要转换成WAV,MP3等格式,才能播放。这个转换过程,是首先将音频编码数据使用对应解码库解码为PCM流,然后再将PCM流根据封装格式的要求,进行编码封装,最后得到可供通用播放器支持的编码和格式。


    音频编码有很多,各有特色,本文首选介绍目前最流行的SILK编码。


    SILK编码最早在Skype中使用,它在编码效率和质量之间取得了很好的平衡,因此被广泛应用在互联网的音频相关产品中,目前广泛使用的是SILK V3。


    腾讯系产品,包括QQ、微信、小程序,在语音相关的实现中,也大量使用到SILK编码,并且,部分功能是直接让未加密的语音流在网络中传输,这是协议还原很感兴趣的部分。毕竟,腾讯的产品防守相当严密,不宜突破。


    WAV作为古老的音频文件格式,虽然有很多缺点,但使用仍然相当广泛,本文将对其关键点进行介绍,SILK编码音频文件解码并封装为WAV格式的方法。


    01

    SILK编码


    SILK采样率可为8、12、16或24 kHz,比特率可为6至40 kbit/s。对应到报文层面的直观印象,即SILK编码的语音数据每帧长度是不等的。


    SILK编码已经开源,目前可下载到的版本为V1.0.9,它是Opus编码的基础。因此可以在Opus编码的源文件中找到SILK编码的实现代码,下载地址如下:

    http://www.opus-codec.org/release/stable/2017/06/26/libopus-1_2_1.html


    当然,其它地方也能很容易找到SILK编码的实现,比如GitHub上:

    640?wx_fmt=png

    当前经常遇到的SILK V3的编码就是SILK编码,在SILK的V1.0.9版SDK中的编码就是SILK V3编码,另外SILK库版本差异对语音解码基本可以忽略。如果各位朋友需要下载SILK编码库,可关注本公众号后发送关键词“silk”获取。


    SILK编码库的功能是提供接口将SILK格式音频数据解码为PCM流以及将PCM流编码为SILk格式。在库内提供了编解码的例子,很容易懂。


    02


    WAV封装格式


    语音编码后,如果一个软件不支持该编码,则语音是无法解码的,因此,语音数据的播放,首先需要的是将编码数据解码。如上一部分中的SILK编码库,就是用来将语音的原始PCM数据编码为SILK编码数据,以及将SILK编码数据解码为PCM数据流的。


    各类通用的音视频播放器,对一个编码的支持,首先是根据文件名称后缀或者文件的内容进行封装格式识别,再根据封装格式中对编码的描述来进行编码的识别,如果编码被识别,并且集成了该编码库,则该编码的语音数据就可以被播放,也就是被支持了。


    在当前技术发展阶段,对音频播放器而言,WAV格式和MP3格式是最基本的封装格式,基本上都会被支持,WAV格式支持原始的音频流即PCM流,这是最简单的格式,只需要将所有语音编码数据解码保存,然后在文件头部加上WAV格式结构体,即可进行封装。


    当然,WAV格式缺点明显,其数据相当于未压缩,体积很大。


    WAV格式头共44字节,定义及描述如下:

    struct WAVHeader {     char Riff[4];     uint32 ChunkSize;     char Format[4];     char SubChunkID[4];     uint32 SubChunkSize;     uint16 AudioFormat;     uint16 NumChannels;     uint32 SampleRate;     uint32 ByteRate;     uint16 BlockAlign;     uint16 BitsPerSample;     char Data[4];     uint32 DataLen; };
    Riff:4字节,固定值“RIFF”。ChunkSize:4字节,整个文件不包含Riff及ChunkSize的8字节。Format:4字节,固定值“WAVE”。SubChunkID:4字节,固定值“fmt ”,注意最后1字节为空格0x20。SubChunkSize:4字节,对应WAV,值为0x10。AudioFormat:2字节,格式类型,线性PCM则值为1,其它值则为压缩的编码格式,如0x06为PCMA,0x07为PCMU。NumChannels:2字节,声道数,1或者2。SampleRate:4字节,采样频率,如11025,44100等。ByteRate:4字节,码率=采样频率*PCM采样位深/8*声道数。BlockAlign:2字节,采样一次占内存的长度=声道数*PCM采样位深/8。BitsPerSample:2字节,PCM采样位深,如8、16。Data:4字节,固定值“data”。DataLen:4字节,PCM数据总长度。

    一个典型的WAV头部数据如下:

    640?wx_fmt=png


    03


    SILK V3转换到WAV


    SILK编码到WAV的转换,当然要用到SILK编码库,供下载的SILK编码库内,提供了4个不同的平台版本,功能基本相同。


    在SILK编码库的SDK内,test目录下,有一个Decoder.c文件,是解码的示例文件,编译后它能够直接解码如下格式的SILK音频文件:

    640?wx_fmt=png

    如上图所示,SILK文件格式以"#!SILK_V3"开始,之后就是一帧帧的语音内容块,每帧的帧长占两字节,然后为帧内容,这是因为SILk编码是变长编码,每帧的长度是不同的。


    在各个test_vectorsitstream目录下的.bit文件,即为SILK格式文件,可以被解码程序Decoder.c支持。


    解码程序将SILK文件解码为PCM流文件,但缺少WAV头,无法直接用播放器播放。


    如果需要使解码后的PCM流能够播放,则需要PCM流前面添加WAV头。对微信语音短消息,WAV头赋值如下(pcmsize为PCM流的字节数):

    whead.DataLen = pcmsize;

    memcpy(whead.Riff,"RIFF",4);

    memcpy(whead.Format,"WAVE",4);

    memcpy(whead.SubChunkID,"fmt ",4);

    memcpy(whead.Data,"data",4);

    whead.ChunkSize =whead.DataLen+44-8;

    whead.SubChunkSize = 0x10;

    whead.AudioFormat = 1;

    whead.NumChannels = 1;

    whead.SampleRate = DecControl.API_sampleRate;

    whead.BitsPerSample = 16;

    whead.BlockAlign = whead.NumChannels*whead.BitsPerSample/8;

    whead.ByteRate = whead.SampleRate*whead.BlockAlign;


    其中NumChannels、SampleRate和BitsPerSample等参数需要根据具体情况进行填充,否则,声音会很怪异。


    对SILK编码转换为WAV语音文件,如果有疑惑,可以咨询我。


    640?wx_fmt=jpeg

    长按进行关注。





  • 相关阅读:
    关于这个 blog
    P6499 [COCI2016-2017#2] Burza 题解
    CF1172F Nauuo and Bug 题解
    CF1479D Odd Mineral Resource 题解
    CF1442E Black, White and Grey Tree 题解
    CF1442D Sum 题解
    CF1025D Recovering BST 题解
    CF1056E Check Transcription 题解
    CF1025F Disjoint Triangles 题解
    红包算法的PHP实现
  • 原文地址:https://www.cnblogs.com/protosec/p/11673358.html
Copyright © 2011-2022 走看看