目录

一、使用场景

二、术语介绍

三、如何使用

四、参考资料


一、使用场景

产品需求是开发一款名叫微信助手的即时通讯APP,安装在汽车中控台上,在行车过程中使用,使用者包括驾驶员与车内其它成员,主要针对驾驶者。此应用接收、发送消息的接口基于第三方微控提供的API,我们根据交互文档在API基础上封装业务逻辑,开发一款定制UI的类似微信的APP,驾驶者在行车过程中可以使用语音进行交互,减少了驾驶的危险性。

微信的语音消息默认格式为silk,所以车机端接收到其它终端发送过来的语音消息是没有办法直接播放的,必须要转成pcm格式才能使用Android的控件进行播放,并且Android录制的pcm数据也需要转成silk发送出去,微信终端才能正常播放,所以这里就涉及到silk和pcm格式的互转,其它有用到silk语音格式的同学也可以参考。

二、术语介绍

1.silk语音格式

silk是一种轻量级,体积小、音质高的一种音频文件格式。

2.pcm语音格式

pcm是语音文件的原始数据,这种数据声卡可以直接播放。

3.SILKCodec库

它是由Skype向第三方开发人员和硬件制造商提供免版税认证(RF)的Silk宽带音频编码器。

三、如何使用

1.silk转pcm

#include <jni.h>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "time.h"/* Define codec specific settings */
#define MAX_BYTES_ENC_PER_FRAME     250 // Equals peak bitrate of 100 kbps
#define MAX_BYTES_DEC_PER_FRAME     1024#define MAX_INPUT_FRAMES        5
#define MAX_LBRR_DELAY          2
#define MAX_FRAME_LENGTH        480#define  MAX_FRAME           160#include <android/log.h>
#include <SKP_Silk_control.h>#define LOG_TAG "silk" // text for log tag#include "SKP_Silk_SDK_API.h"
#include "SKP_Silk_SigProc_FIX.h"#undef DEBUG_SILK8// the header length of the RTP frame (must skip when en/decoding)
#define RTP_HDR_SIZE    12/
#define LOG_I(TAG, ...)    __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOG_E(TAG, ...)    __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)#define TAG "SILK"
#define ERROR_BAD_VALUE -2#define MAX_BYTES_PER_FRAME     1024
//#define MAX_INPUT_FRAMES        5
#define FRAME_LENGTH_MS         20
#define MAX_API_FS_KHZ          48
/static int codec_open = 0;static JavaVM *gJavaVM;
const char *kInterfacePath = "org/sipdroid/pjlib/silk8";/* encoder parameters */SKP_int32 encSizeBytes;
void      *psEnc;/* default settings */
SKP_int   fs_kHz = 8;
SKP_int   targetRate_bps = 20000;
SKP_int   packetSize_ms = 20;
SKP_int   frameSizeReadFromFile_ms = 20;
SKP_int   packetLoss_perc = 0, smplsSinceLastPacket;
SKP_int   INBandFec_enabled = 0, DTX_enabled = 0, quiet = 0;
SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoder/* decoder parameters */jbyte payloadToDec[    MAX_BYTES_DEC_PER_FRAME * MAX_INPUT_FRAMES * ( MAX_LBRR_DELAY + 1 ) ];
jshort out[ ( MAX_FRAME_LENGTH << 1 ) * MAX_INPUT_FRAMES ], *outPtr;
SKP_int32 decSizeBytes;
void      *psDec;
SKP_SILK_SDK_DecControlStruct DecControl;
//silk->pcm
//inputPath_    //silk文件输入路径
//sampleRate    //微信采用8000
//outputPath_   //pcm文件输出路径
extern "C"
JNIEXPORT jboolean JNICALL Java_com_cneeds_silkcodec_lib_SILK8_silk2PCM(JNIEnv *env, jobject obj,jstring inputPath_,jint sampleRate,jstring outputPath_){const char *inputPath = env->GetStringUTFChars(inputPath_, 0);const char *outputPath = env->GetStringUTFChars(outputPath_, 0);unsigned long totTime, startTime;double fileLength;size_t counter;SKP_int32 ret, tot_len, totPackets;SKP_int32 decSizeBytes, frames, packetSize_ms = 0;SKP_int16 nBytes, len;SKP_uint8 payload[MAX_BYTES_PER_FRAME * MAX_INPUT_FRAMES], *payloadToDec = NULL;SKP_int16 out[((FRAME_LENGTH_MS * MAX_API_FS_KHZ) << 1) * MAX_INPUT_FRAMES], *outPtr;void *psDec;FILE *inFile, *outFile;SKP_SILK_SDK_DecControlStruct DecControl;LOG_I(TAG, "********** Silk Decoder (Fixed Point) v %s ********************",SKP_Silk_SDK_get_version());LOG_I(TAG, "********** Compiled for %d bit cpu *******************************",(int) sizeof(void *) * 8);LOG_I(TAG, "Input:                       %s", inputPath);LOG_I(TAG, "Output:                      %s", outputPath);// 打开输入文件inFile = fopen(inputPath, "rb");if (inFile == NULL) {LOG_E(TAG, "Error: could not open input file %s", inputPath);return false;}// 验证文件头(微信silk头文件)LOG_I(TAG, "验证微信silk头文件[.#!SILK_V3]");LOG_I(TAG, "验证微信silk头文件需要跳过第一个字节 begin");fseek(inFile,1,0);LOG_I(TAG, "验证微信silk头文件需要跳过第一个字节 end");//验证文件头{char header_buf[50];fread(header_buf, sizeof(char), strlen("#!SILK_V3"), inFile);header_buf[strlen("#!SILK_V3")] = '\0';if (strcmp(header_buf, "#!SILK_V3") != 0) {LOG_E(TAG, "Error: Wrong Header %s", header_buf);return false;}LOG_I(TAG, "Header is \"%s\"", header_buf);}// 打开输出文件outFile = fopen(outputPath, "wb");if (outFile == NULL) {LOG_E(TAG, "Error: could not open output file %s", outputPath);return false;}// 设置采样率if (sampleRate == 0) {DecControl.API_sampleRate = 8000;} else {DecControl.API_sampleRate = sampleRate;}// 获取 Silk 解码器状态的字节大小ret = SKP_Silk_SDK_Get_Decoder_Size(&decSizeBytes);if (ret) {LOG_E(TAG, "SKP_Silk_SDK_Get_Decoder_Size returned %d", ret);}psDec = malloc((size_t) decSizeBytes);// 初始化或充值解码器ret = SKP_Silk_SDK_InitDecoder(psDec);if (ret) {LOG_E(TAG, "SKP_Silk_SDK_InitDecoder returned %d", ret);}totPackets = 0;totTime = 0;while (1) {// 读取有效数据大小counter = fread(&nBytes, sizeof(SKP_int16), 1, inFile);if (nBytes < 0 || counter < 1) {break;}// 读取有效数据counter = fread(payload, sizeof(SKP_uint8), (size_t) nBytes, inFile);if ((SKP_int16) counter < nBytes) {break;}payloadToDec = payload;outPtr = out;tot_len = 0;startTime = GetHighResolutionTime();frames = 0;do {// 解码ret = SKP_Silk_SDK_Decode(psDec, &DecControl, 0, payloadToDec, nBytes, outPtr, &len);if (ret) {LOG_E(TAG, "SKP_Silk_SDK_Decode returned %d", ret);}frames++;outPtr += len;tot_len += len;if (frames > MAX_INPUT_FRAMES) {outPtr = out;tot_len = 0;frames = 0;}} while (DecControl.moreInternalDecoderFrames);packetSize_ms = tot_len / (DecControl.API_sampleRate / 1000);totTime += GetHighResolutionTime() - startTime;totPackets++;// 将解码后的数据保存到文件fwrite(out, sizeof(SKP_int16), (size_t) tot_len, outFile);}LOG_I(TAG, "Packets decoded:             %d", totPackets);LOG_I(TAG, "Decoding Finished");free(psDec);fclose(outFile);fclose(inFile);fileLength = totPackets * 1e-3 * packetSize_ms;LOG_I(TAG, "File length:                 %.3f s", fileLength);LOG_I(TAG, "Time for decoding:           %.3f s (%.3f%% of realTime)", 1e-6 * totTime,1e-4 * totTime / fileLength);env->ReleaseStringUTFChars(inputPath_, inputPath);env->ReleaseStringUTFChars(outputPath_, outputPath);return true;
}

2.pcm转silk


//pcm->silk
//inputPath_    //pcm文件输入路径
//outputPath_   //sil文件输出路径
extern "C"
JNIEXPORT jboolean JNICALL Java_com_cneeds_silkcodec_lib_SILK8_pcm2SILK(JNIEnv *env, jobject obj,jstring inputPath_,jstring outputPath_){const char *inputPath = env->GetStringUTFChars(inputPath_, 0);const char *outputPath = env->GetStringUTFChars(outputPath_, 0);LOG_I(TAG, "********** Silk Decoder (Fixed Point) v %s ********************",SKP_Silk_SDK_get_version());LOG_I(TAG, "********** Compiled for %d bit cpu *******************************",(int) sizeof(void *) * 8);LOG_I(TAG, "Input:                       %s", inputPath);LOG_I(TAG, "Output:                      %s", outputPath);unsigned long tottime, starttime;double    filetime;size_t    counter;SKP_int32 k, args, totPackets, totActPackets, ret;SKP_int16 nBytes;double    sumBytes, sumActBytes, avg_rate, act_rate, nrg;SKP_uint8 payload[ MAX_BYTES_PER_FRAME * MAX_INPUT_FRAMES ];SKP_int16 in[ FRAME_LENGTH_MS * MAX_API_FS_KHZ * MAX_INPUT_FRAMES ];
//    char      speechInFileName[ 150 ], bitOutFileName[ 150 ];FILE      *bitOutFile, *speechInFile;SKP_int32 encSizeBytes;void      *psEnc;
#ifdef _SYSTEM_IS_BIG_ENDIANSKP_int16 nBytes_LE;
#endif/* default settings */SKP_int32 API_fs_Hz = 8000;SKP_int32 max_internal_fs_Hz = 0;SKP_int32 targetRate_bps = 25000; //?SKP_int32 smplsSinceLastPacket, packetSize_ms = 20;SKP_int32 frameSizeReadFromFile_ms = 20;SKP_int32 packetLoss_perc = 0;SKP_int32 complexity_mode = 0;SKP_int32 DTX_enabled = 0, INBandFEC_enabled = 0, quiet = 0;SKP_SILK_SDK_EncControlStruct encControl; // Struct for input to encoderSKP_SILK_SDK_EncControlStruct encStatus;  // Struct for status of encoder/* If no max internal is specified, set to minimum of API fs and 24 kHz */if( max_internal_fs_Hz == 0 ) {max_internal_fs_Hz = 24000;if( API_fs_Hz < max_internal_fs_Hz ) {max_internal_fs_Hz = API_fs_Hz;}}/* Open files */speechInFile = fopen( inputPath, "rb" );if( speechInFile == NULL ) {LOG_E(TAG, "Error: could not open input file %s", inputPath);return false;}bitOutFile = fopen( outputPath, "wb" );if( bitOutFile == NULL ) {LOG_E(TAG, "Error: could not open output file %s", outputPath);return false;}/* Add Silk header to stream */{//微信语音第一个字节是2jbyte j = 2;fwrite(&j, sizeof(jbyte), 1, bitOutFile );static const char Silk_header[] = "#!SILK_V3";fwrite( Silk_header, sizeof( char ), strlen( Silk_header ), bitOutFile );}/* Create Encoder */ret = SKP_Silk_SDK_Get_Encoder_Size( &encSizeBytes );if( ret ) {LOG_E(TAG, "SKP_Silk_SDK_Get_Encoder_Size returned %d", ret);}psEnc = malloc( encSizeBytes );/* Reset Encoder */ret = SKP_Silk_SDK_InitEncoder( psEnc, &encStatus );if( ret ) {LOG_E(TAG, "SKP_Silk_SDK_InitEncoder returned %d", ret);}/* Set Encoder parameters */encControl.API_sampleRate        = API_fs_Hz;encControl.maxInternalSampleRate = max_internal_fs_Hz;encControl.packetSize            = ( packetSize_ms * API_fs_Hz ) / 1000;encControl.packetLossPercentage  = packetLoss_perc;encControl.useInBandFEC          = INBandFEC_enabled;encControl.useDTX                = DTX_enabled;encControl.complexity            = complexity_mode;encControl.bitRate               = ( targetRate_bps > 0 ? targetRate_bps : 0 );if( API_fs_Hz > MAX_API_FS_KHZ * 1000 || API_fs_Hz < 0 ) {LOG_E(TAG, "Error: API sampling rate = %d out of range, valid range 8000 - 48000", API_fs_Hz);}tottime              = 0;totPackets           = 0;totActPackets        = 0;smplsSinceLastPacket = 0;sumBytes             = 0.0;sumActBytes          = 0.0;smplsSinceLastPacket = 0;while( 1 ) {/* Read input from file */counter = fread( in, sizeof( SKP_int16 ), ( frameSizeReadFromFile_ms * API_fs_Hz ) / 1000, speechInFile );
#ifdef _SYSTEM_IS_BIG_ENDIANswap_endian( in, counter );
#endifif( ( SKP_int )counter < ( ( frameSizeReadFromFile_ms * API_fs_Hz ) / 1000 ) ) {break;}/* max payload size */nBytes = MAX_BYTES_PER_FRAME * MAX_INPUT_FRAMES;starttime = GetHighResolutionTime();/* Silk Encoder */ret = SKP_Silk_SDK_Encode( psEnc, &encControl, in, (SKP_int16)counter, payload, &nBytes );if( ret ) {printf( "\nSKP_Silk_Encode returned %d", ret );}tottime += GetHighResolutionTime() - starttime;/* Get packet size */packetSize_ms = ( SKP_int )( ( 1000 * ( SKP_int32 )encControl.packetSize ) / encControl.API_sampleRate );smplsSinceLastPacket += ( SKP_int )counter;if( ( ( 1000 * smplsSinceLastPacket ) / API_fs_Hz ) == packetSize_ms ) {/* Sends a dummy zero size packet in case of DTX period  *//* to make it work with the decoder test program.        *//* In practice should be handled by RTP sequence numbers */totPackets++;sumBytes  += nBytes;nrg = 0.0;for( k = 0; k < ( SKP_int )counter; k++ ) {nrg += in[ k ] * (double)in[ k ];}if( ( nrg / ( SKP_int )counter ) > 1e3 ) {sumActBytes += nBytes;totActPackets++;}/* Write payload size */
#ifdef _SYSTEM_IS_BIG_ENDIANnBytes_LE = nBytes;swap_endian( &nBytes_LE, 1 );fwrite( &nBytes_LE, sizeof( SKP_int16 ), 1, bitOutFile );
#elsefwrite( &nBytes, sizeof( SKP_int16 ), 1, bitOutFile );
#endif/* Write payload */fwrite( payload, sizeof( SKP_uint8 ), nBytes, bitOutFile );smplsSinceLastPacket = 0;if( !quiet ) {fprintf( stderr, "\rPackets encoded:                %d", totPackets );}}}/* Write dummy because it can not end with 0 bytes */nBytes = -1;/* Write payload size */fwrite( &nBytes, sizeof( SKP_int16 ), 1, bitOutFile );/* Free Encoder */free( psEnc );fclose( speechInFile );fclose( bitOutFile   );filetime  = totPackets * 1e-3 * packetSize_ms;avg_rate  = 8.0 / packetSize_ms * sumBytes       / totPackets;act_rate  = 8.0 / packetSize_ms * sumActBytes    / totActPackets;env->ReleaseStringUTFChars(inputPath_, inputPath);env->ReleaseStringUTFChars(outputPath_, outputPath);return true;
}

四、参考资料

1.SILKCodec源码:

https://github.com/hncsJackchen/SILKCodec

2.SilkSDK 源码

Silk和PCM数据之间的换转相关推荐

  1. 关于音频PCM数据2字节(16位)byte与64位double之间的转换

    1 致谢 感谢kimmking网友提供的资料 原文链接如下:http://blog.csdn.net/kimmking/article/details/8752737 2 问题描述 今天遇到一个问题 ...

  2. android中调用fft函数,J使用PCM数据在Android中转换FFT(JTransforms FFT in Android from PCM data)...

    J使用PCM数据在Android中转换FFT(JTransforms FFT in Android from PCM data) 我一直在玩这个游戏已经有一段时间了,我无法弄清楚我在这里要做的事情. ...

  3. JavaScript基础修炼(14)——WebRTC在浏览器中如何获得指定格式的PCM数据【华为云分享】

    [摘要] WebRTC音频处理基础知识及实战 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 本 ...

  4. JavaScript基础修炼(14)——WebRTC在浏览器中如何获得指定格式的PCM数据

    我的github主页:https://github.com/dashnowords 我的新书上架啦,3天即登京东计算机编程语言类排行榜Top1!!!精选30+JavaScript库,从使用方式,设计原 ...

  5. 树莓派上做音频采集,采集pcm数据并直接保存pcm数据

    前言 看了很长时间关于alsa音频驱动的一些中英文资料,终于把这个程序给搞出来了.这个程序实现的是在树莓派上通过usb音频采集卡进行音频采集,并直接将pcm数据保存起来,通过c语言实现. 项目地址 a ...

  6. ROS与PCL中点云数据之间的转换

    此为文章初稿还没有完善,应该还有一些问题,等待后面有时间再继续更新,原创文章,未经允许,请勿转载!!! 首先介绍在PCL库中经常使用的两种点云之间的转换,这里将根据工程中的经验,从代码层面举例分析如何 ...

  7. 1小时学会:最简单的iOS直播推流(五)yuv、pcm数据的介绍和获取

    最简单的iOS 推流代码,视频捕获,软编码(faac,x264),硬编码(aac,h264),美颜,flv编码,rtmp协议,陆续更新代码解析,你想学的知识这里都有,愿意懂直播技术的同学快来看!! 源 ...

  8. python使用matplotlib可视化雷达图(polar函数可视化雷达图、极坐标图、通过径向方向来显示数据之间的关系)

    python使用matplotlib可视化雷达图(polar函数可视化雷达图.极坐标图.通过径向方向来显示数据之间的关系) 目录

  9. 科普丨数据中心、云计算、大数据之间有什么区别和联系?

    不少人把数据中心.云计算数据中心.大数据搞混淆,觉得这三者是一样的产品,其实有显著地区别,数据中心机房是一整套复杂的设施,如今,云计算即将成为信息社会的公共资源,而数据中心则是支撑云计算服务的基础设施 ...

最新文章

  1. WinForm导出文件,你懂的……
  2. Nat. Biotech.|药物设计的AI生成模型
  3. Asp.net 2.0 中获取控件输出的Html代码 (转)
  4. 如何在Kali Linux中安装Google Chrome浏览器
  5. 员工管理系统---SpringBoot
  6. 物尽其用-让推荐系统成为你学习的助手
  7. MongoDB怎么做性能测试,看看这篇大神总结
  8. storyboard用代码跳转
  9. 从mediaserver入手快速理解binder机制(最简单理解binder)
  10. 【ACL2020】最新效果显著的关系抽取框架了解一下?
  11. 常见黑客渗透测试工具
  12. 萤石云设备下线是什么导致的_萤石设备突然看不了,提示不在线怎么办?
  13. 计算机设备属于什么会计科目,​机器设备属于什么会计科目
  14. MySQL INSERT对表中数据的操作-插入数据
  15. python interpreter下载_Piton - Python interpreter
  16. java模拟抛硬币_用随机数模拟抛硬币
  17. Android各种时间格式转换
  18. RTL8211F 硬件配置
  19. 19.jvm内存结构部分——堆_内存诊断_jmap
  20. 【CSS】1203- 分享 20 个防御式 CSS 开发经验

热门文章

  1. 山西省审计计算机培训,山西省审计厅审计管理系统(OA)正式投入使用
  2. Centos用parted分区超过2TB硬盘-分区格式化
  3. (HDU2031 C++) 进制转换
  4. 钨酸铋量子点/纳米片修饰石墨相氮化碳(g-C3N4)(Bi2WO6/g-C3N4)/CdTe/SiO2/PMMA复合粒子应用
  5. LM339-----比较器,以及工程应用实践(比较详细)
  6. 计算机在机械制造领域中的应用论文,计算机技术在机械制造领域的应用论文
  7. VRML(Virtual Reality Modeling Language)即虚拟现实建模语言
  8. axios get 方法后台没有得到传递的参数
  9. 计算机的软盘有没有磁性材料,磁性材料竟然还有如此惊人的历史!
  10. python趣味编程代码大全_Python趣味编程 20行代码实现读心术