文章目录

  • 初识 Speex 语音压缩
    • Speex 简介
    • Speex 性能
    • Speex 与 Opus
    • Speex 的 JNI 实现

初识 Speex 语音压缩

Speex 简介

Speex 是一个声音编码格式,目标是用于网络电话、在线广播使用的语音编码,基于 CELP(一种语音编码算法)开发,Speex 宣称可以免费使用,以 BSD 授权条款开放源代码。

Speex 的开发者将这个格式视为 Vorbis(通用音频压缩格式)的补充。

Speex 是一种有损格式,这意味着使用此格式的音频,质量将会永久性地降低以减少文件的大小。

开发 Speex 的 Xiph.org 基金会已经宣布废弃 Speex,建议改用 Opus 取代。

Speex 是针对网际协议通话技术(VoIP)和基于文件的压缩。Speex 的设计目标是开发一个保有高质量语音的同时降低其比特率(bit rate)的编码器。 为了实现前述目标,Speex 编码器使用多位比特率(multiple bit rates),并支持超宽频 (32 kHz 采样率),宽带 (16 kHz 采样率) 和窄带 (电话通话质量,8 kHz 采样率)。 由于 Speex 是设计用于 VoIP 而不是手机,因此 Speex 编码器必须能容忍丢失数据包(lost packets),但不能数据包是损坏的。基于上述的要求,选择 CELP 算法作为 Speex 的编码技术。使用 CELP 的主要原因之一是,CELP 早已证明,它可以同时做到低比特率和高比特率。

Speex 的主要特性归纳如下:

  • 自由软件 / 开源,无专利保护且使用无需版税。
  • 集窄带和宽带在同一比特流(bit-stream)。
  • 比特率可选择的范围很广 (从 2 kbit/s 至 44 kbit/s)。
  • 动态交换的比特率和可变比特率 (VBR, variable bit-rate)。
  • 语音动态的检测 (VAD,与 VBR 集成)(1.2 版开始废弃)。

Speex 性能

目前只测了部分压缩的性能(设备:一加3T【高通骁龙821】)

Speex 与 Opus

Opus 是一个有损声音编码的格式,由 Xiph.Org 基金会开发,之后由互联网工程任务组进行标准化,目标是希望用单一格式包含声音和语音,取代 Speex 和 Vorbis,且适用于网络上低延迟的即时声音传输,标准格式定义于 RFC 6716 文件。Opus 格式是一个 开放格式,使用上没有任何专利或限制。

Opus 集成了两种声音编码的技术:以语音编码为导向的 SILK 和低延迟的 CELT。Opus 可以无缝调节高低 比特率。在编码器内部它在较低比特率时使用线性预测编码在高比特率时候使用变换编码(在高低比特率交界处也使用两者结合的编码方式)。Opus 具有非常低的算法延迟(默认为 22.5 ms)[3],非常适合用于低延迟语音通话的编码,像是网络上的即时声音流、即时同步声音旁白等等,此外 Opus 也可以透过降低编码比特率,达成更低的算法延迟,最低可以到 5 ms。在多个听觉盲测中,Opus 都比 MP3、AAC、HE-AAC 等常见格式,有更低的延迟和更好的声音压缩率。

所以,临时用用可以考虑 Speex。如果要正式上线,还是考虑使用 Opus

Speex 的 JNI 实现

Speex 代码可以从 Speex 官网 下载,目前最新版本为 v1.2.0 (可能以后都不会更新了)

Speex 源码是 C 语音版本的。

自己使用的时候,加了一套 API 方便调用。

// speex_codec.h
#include "speex/speex.h"namespace speex {class SpeexCodec {public:enum CodecMode {ENCODE,         // 编码,压缩DECODE,         // 解码,解压};enum Quality {// [1 - 15]QUALITY_START,QUALITY_1,  QUALITY_2,  QUALITY_3,  QUALITY_4,  QUALITY_5,QUALITY_6,  QUALITY_7,  QUALITY_8,  QUALITY_9,  QUALITY_10,QUALITY_11, QUALITY_12, QUALITY_13, QUALITY_14, QUALITY_15,QUALITY_END,};enum Complexity {// [1 - 8]COMPLEXITY_START,COMPLEXITY_1, COMPLEXITY_2, COMPLEXITY_3, COMPLEXITY_4,COMPLEXITY_5, COMPLEXITY_6, COMPLEXITY_7, COMPLEXITY_8,COMPLEXITY_END,};enum ModeId {MODEID_NB  = SPEEX_MODEID_NB,   // narrowband 窄宽带模式MODEID_WB  = SPEEX_MODEID_WB,   // wideband 宽带模式MODEID_UWB = SPEEX_MODEID_UWB,  // wideband 超宽带模式};SpeexCodec(CodecMode  codecMode,int        decSize,int        encSize,Quality    quality,Complexity complexity,ModeId     modeId);~SpeexCodec();int encode(short * dec, int decSize, int * useDecSize, char * enc);int decode(char * enc, int encSize, int * useEncSize, short * dec);void reset();CodecMode  getCodecMode();Quality    getQuality();Complexity getComplexity();ModeId     getModeId();int        getDecSize();int        getEncSize();private:SpeexCodec(){}SpeexCodec(const SpeexCodec&){}SpeexCodec(const SpeexCodec&&){}SpeexCodec& operator = (const SpeexCodec&){ return *this; }CodecMode  mCodecMode;Quality    mQuality;Complexity mComplexity;ModeId     mModeId;int        mDecSize;int        mEncSize;void *     mState;SpeexBits  mBits;};
}
// speex_codec.cpp
#include "speex_codec.h"namespace speex {SpeexCodec::SpeexCodec(CodecMode  codecMode,int        decSize,int        encSize,Quality    quality,Complexity complexity,ModeId     modeId) {this->mCodecMode  = codecMode;this->mDecSize    = decSize;this->mEncSize    = encSize;this->mQuality    = quality;this->mComplexity = complexity;this->mModeId     = modeId;if ((this->mQuality <= QUALITY_START || this->mQuality >= QUALITY_END)|| (this->mDecSize <= 0)) {ASSERT_EXIT();}switch (this->mCodecMode) {case ENCODE:if (this->mComplexity <= COMPLEXITY_START || this->mComplexity >= COMPLEXITY_END) {ASSERT_EXIT();}this->mState = speex_encoder_init(speex_lib_get_mode(this->mModeId));speex_encoder_ctl(this->mState, SPEEX_SET_QUALITY, &this->mQuality);speex_encoder_ctl(this->mState, SPEEX_SET_COMPLEXITY, &this->mComplexity);break;case DECODE:this->mState = speex_decoder_init(speex_lib_get_mode(this->mModeId));break;default:ASSERT_EXIT();}speex_bits_init(&this->mBits);}SpeexCodec::~SpeexCodec() {speex_bits_destroy(&this->mBits);switch (this->mCodecMode) {case ENCODE:speex_encoder_destroy(this->mState);break;case DECODE:speex_decoder_destroy(this->mState);break;default:ASSERT_EXIT();}}int SpeexCodec::encode(short * dec, int decSize, int * useDecSize, char * enc) {if (useDecSize != nullptr) {*useDecSize = 0;}if (dec == nullptr || enc == nullptr || decSize <= 0 || this->mDecSize <= 0|| this->mCodecMode != ENCODE) {return -1;}int encLen = 0;{int n = decSize / this->mDecSize;if (n > 0) {int max_nbytes = (this->mEncSize > 0) ? this->mEncSize : this->mDecSize;for (int i = 0; i < n; ++ i) {speex_bits_reset(&this->mBits);speex_encode_int(this->mState, dec + i * this->mDecSize, &this->mBits);encLen += speex_bits_write(&this->mBits, enc + encLen, max_nbytes);}}if (useDecSize != nullptr) {*useDecSize = n * this->mDecSize;}}return encLen;}int SpeexCodec::decode(char * enc, int encSize, int * useEncSize, short * dec) {if (useEncSize != nullptr) {*useEncSize = 0;}if (enc == nullptr || dec == nullptr || encSize <= 0 || this->mEncSize <= 0|| this->mDecSize <= 0 || this->mCodecMode != DECODE) {return -1;}int decLen = 0;{int n = encSize / this->mEncSize;if (n > 0) {for (int i = 0; i < n; ++ i) {speex_bits_reset(&this->mBits);speex_bits_read_from(&this->mBits, enc + i * this->mEncSize, this->mEncSize);speex_decode_int(this->mState, &this->mBits, dec + decLen);decLen += this->mDecSize;}}if (useEncSize != nullptr) {*useEncSize = n * this->mEncSize;}}return decLen;}void SpeexCodec::reset() {int eph = 0;switch (this->mCodecMode) {case ENCODE:speex_encoder_ctl(this->mState, SPEEX_RESET_STATE, nullptr);break;case DECODE:speex_decoder_ctl(this->mState, SPEEX_RESET_STATE, nullptr);speex_decoder_ctl(this->mState, SPEEX_SET_ENH, &eph);break;default:ASSERT_EXIT();}}SpeexCodec::CodecMode SpeexCodec::getCodecMode() {return this->mCodecMode;}SpeexCodec::Quality SpeexCodec::getQuality() {return this->mQuality;}SpeexCodec::Complexity SpeexCodec::getComplexity() {return this->mComplexity;}SpeexCodec::ModeId SpeexCodec::getModeId() {return this->mModeId;}int SpeexCodec::getDecSize() {return this->mDecSize;}int SpeexCodec::getEncSize() {return this->mEncSize;}
}
#include <jni.h>
#include "speex_codec.h"static speex::SpeexCodec * gmpSpeexCodecEncode = nullptr;
static speex::SpeexCodec * gmpSpeexCodecDecode = nullptr;
static bool gmSpeexCodecOpen = false;extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniOpen(JNIEnv *env, jclass , jint quality, jint complexity, jint modeId, jint decSize, jint encSize) {if (gmSpeexCodecOpen) {return (jint)-1;}gmSpeexCodecOpen = true;gmpSpeexCodecEncode = new speex::SpeexCodec(speex::SpeexCodec::ENCODE, decSize, encSize,(speex::SpeexCodec::Quality) quality,(speex::SpeexCodec::Complexity) complexity,(speex::SpeexCodec::ModeId) modeId);gmpSpeexCodecDecode = new speex::SpeexCodec(speex::SpeexCodec::DECODE, decSize, encSize,(speex::SpeexCodec::Quality) quality,(speex::SpeexCodec::Complexity) complexity,(speex::SpeexCodec::ModeId) modeId);if (gmpSpeexCodecEncode == nullptr || gmpSpeexCodecDecode == nullptr) {if (gmpSpeexCodecEncode != nullptr) {delete gmpSpeexCodecEncode;gmpSpeexCodecEncode = nullptr;}if (gmpSpeexCodecDecode != nullptr) {delete gmpSpeexCodecDecode;gmpSpeexCodecDecode = nullptr;}return -1;}return (jint)0;
}extern "C"
JNIEXPORT jlong Java_org_zone_speex_Speex_JniEncode(JNIEnv *env, jclass, jshortArray jdec, jint jdecSize, jbyteArray jenc) {if (!gmSpeexCodecOpen || gmpSpeexCodecEncode == nullptr) {return -1;}jshort * dec = env->GetShortArrayElements(jdec, 0);jbyte * enc = env->GetByteArrayElements(jenc, 0);int useDecSize = 0, useEncSize = 0;useEncSize = gmpSpeexCodecEncode->encode((short *)dec, jdecSize, &useDecSize, (char *)enc);env->ReleaseShortArrayElements(jdec, dec, 0);env->ReleaseByteArrayElements(jenc, enc, 0);return (jlong)((((long)useDecSize) << 32L) | ((long)useEncSize & 0xFFFFFFFFL));
}extern "C"
JNIEXPORT jlong Java_org_zone_speex_Speex_JniDecode(JNIEnv *env, jclass, jbyteArray jenc, jint jencSize, jshortArray jdec) {if (!gmSpeexCodecOpen || gmpSpeexCodecEncode == nullptr) {return -1;}jshort * dec = env->GetShortArrayElements(jdec, 0);jbyte * enc = env->GetByteArrayElements(jenc, 0);int useDecSize = 0, useEncSize = 0;useDecSize = gmpSpeexCodecDecode->decode((char *)enc, jencSize, &useEncSize, (short *)dec);env->ReleaseShortArrayElements(jdec, dec, 0);env->ReleaseByteArrayElements(jenc, enc, 0);return (jlong)((((long)useEncSize) << 32L) | ((long)useDecSize & 0xFFFFFFFFL));
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniClose(JNIEnv *env, jclass) {if (! gmSpeexCodecOpen) {LOGE("SpeexCodec not open !");return -1;}if (gmpSpeexCodecEncode != nullptr) {delete gmpSpeexCodecEncode;gmpSpeexCodecEncode = nullptr;}if (gmpSpeexCodecDecode == nullptr) {delete gmpSpeexCodecDecode;gmpSpeexCodecDecode = nullptr;}gmSpeexCodecOpen = false;return (jint)0;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniReset(JNIEnv *env, jclass) {if (! gmSpeexCodecOpen) {LOGE("SpeexCodec not open !");return -1;}if (gmpSpeexCodecEncode != nullptr) {gmpSpeexCodecEncode->reset();}if (gmpSpeexCodecDecode == nullptr) {gmpSpeexCodecDecode->reset();}return (jint)0;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniGetDecSize(JNIEnv *env, jclass) {int ret = -1;if (!gmSpeexCodecOpen || gmpSpeexCodecEncode == nullptr) {;} else {ret = gmpSpeexCodecEncode->getDecSize();}return ret;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniGetEncSize(JNIEnv *env, jclass) {int ret = -1;if (! gmSpeexCodecOpen || gmpSpeexCodecEncode == nullptr) {;} else {ret = gmpSpeexCodecEncode->getEncSize();}return ret;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniGetQuality(JNIEnv *env, jclass) {int ret = -1;if (! gmSpeexCodecOpen) {LOGE("SpeexCodec not open !");} else if (gmpSpeexCodecEncode == nullptr) {LOGE("SpeexCodecEncode invalid !");} else {ret = gmpSpeexCodecEncode->getQuality();}return ret;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniGetComplexity(JNIEnv *env, jclass) {int ret = -1;if (! gmSpeexCodecOpen) {LOGE("SpeexCodec not open !");} else if (gmpSpeexCodecEncode == nullptr) {LOGE("SpeexCodecEncode invalid !");} else {ret = gmpSpeexCodecEncode->getComplexity();}return ret;
}extern "C"
JNIEXPORT jint JNICALL Java_org_zone_speex_Speex_JniGetModeId(JNIEnv *env, jclass) {int ret = -1;if (! gmSpeexCodecOpen) {LOGE("SpeexCodec not open !");} else if (gmpSpeexCodecEncode == nullptr) {LOGE("SpeexCodecEncode invalid !");} else {ret = gmpSpeexCodecEncode->getModeId();}return ret;
}

初识 Speex 语音压缩相关推荐

  1. 语音压缩:压缩率和比特率

    原始语音信号: 64 kbps = 8000 samples/second * 8 bits/sample 语音压缩中: 2.4 kbps = 8000 samples/second * 0.3 bi ...

  2. 北斗开放平台,更加核心的语音压缩功能

    一.语音压缩解压库 语音压缩解压库为钒星北斗开放平台的一项重要功能,其通过突破高压缩比的语音编码和解码技术,设计低延时的语音数据调度协议,实现用户间点对点连续语音对讲,以及用户和后台.用户间即时回传. ...

  3. speex语音增强(去噪)算法简介

    speex的语音增强(去噪)算法介绍 speex是一套主要针对语音的开源免费,无专利保护的应用集合,它不仅包括编解码器,还包括VAD(语音检测), DTX(不连续传输),AEC(回声消除),NS(去噪 ...

  4. 关于SPEEX和语音的研究(转载的基础上加原创)

    前言 项目需要,抛弃掉原有的音频,统一使用speex(虽然这个解决方案也比较老,speex项目已经停止,但是资料最全,能快速满足项目需求的speex最优) speex speex官网 自行了解spee ...

  5. 音频压缩工具——Speex的使用(AndroidiOS语音录音技术预演)

    要在Android/iOS端实现语音对讲,原型为微信与米聊,开始预演所用技术,找到以下资料. 在Android开发中,需要录音并发送到对方设备上.这时问题来了,手机常会是GPRS.3G等方式上网,所以 ...

  6. 基于DSP技术的多路语音实时采集与压缩处理系统

    在语音的数字通信和数字存储等应用领域,需要对多路语音信号进行实时采集和压缩处理[1].如某语音记录设备,需要对8路语音信号进行实时采集和压缩处理,而且要求对采集到的语音数据的压缩率尽量高.一般地,当要 ...

  7. 在3kbps的带宽下还能清晰地语音聊天?

    正文字数:1353  阅读时长:2分钟 Google最新推出的音频编解码器Lyra,不仅实现了以每秒3kb网络宽带提供听起来自然清晰的语音聊天,还可以在仅90ms延迟的情况下在从高端云服务器到中端智能 ...

  8. 实例分享--告诉你如何使用语音和自然语言控制智能家居

       ZigBee作为一种短距离.低功耗的无线通信局域网协议,其优点是超低功耗.安全性高和自组网,并且可容纳多个设备,因此在智能家居控制中占有很大的优势.    但是,仅仅使用ZigBee技术来控制家 ...

  9. HTML是一种标识语音,HTML实现网页端语音输入(语音识别,语义理解,olami)

    olami开放平台sdk除了支持语音识别功能外,更强大的在于支持语义理解功能,在Android平台和iOS平台都有示例demo供大家下载. 在web端,基于JavaScript用olami开放平台sd ...

  10. 视频和语音播放(进行中)

    点播,直播,VR(录制音频和视频,在线的和本地的),视频播放器和音乐播放器(播放音乐和视频),视频和语音 VideoView并不是支持播放所有格式的视频,它仅支持mp4和3gp格式的文件,实际上它的本 ...

最新文章

  1. mysql性能优化之sql语句优化最强合集
  2. linux卸载dev中的设备,Linux /dev 常见特殊设备介绍与应用
  3. ​Mysql数据读写分离(多实例配置)
  4. 【译】Analyzing Token Sale Models
  5. html 图片点击查看大图_【神游千年,大美敦煌】北魏-260窟【高清大图】
  6. JAVA自学笔记22
  7. 2020年深度学习调参技巧合集
  8. C/C++ 不可不知道的编程技巧!第一部分
  9. mysql改单行数据编码_mysql数据库字符编码修改
  10. linux 串口中断_1600字干货 | 大佬讲Linux启动流程(内含福利)
  11. 使用了未初始化的局部变量_Java 成员变量和局部变量
  12. Exchange 跨林迁移 Part4 双方域管理员加到对方管理员组
  13. Centos 7安装和配置 ElasticSearch入门小白
  14. [软件更新]影拓三代 PTZ系列 系列 驱动程序 V6.1.1-3{链接已修复}
  15. 关于小波分解的滤波器理解
  16. QQ互联开发者信息认证
  17. MySql5.6操作时报错:You must SET PASSWORD before execut
  18. Laravel文档梳理7、视图
  19. 0xc0000225无法进系统_0xc0000225无法进系统 - 卡饭网
  20. 高德地图中自定义色块标识任意省市县区域

热门文章

  1. WordPress非组件实现文章阅读次数-不会php还逞强不用组件的我把网站给玩坏了 ( ̄ー ̄〃)
  2. asp 文件上传(ASPUpload组件上传)
  3. 五种对称加密算法总结
  4. resin设置权限_如何配置resin 3.1.9
  5. 0基础参加数学建模,最大程度冲击奖项
  6. northwind数据库介绍
  7. froglt教你使用色相环配色(原创理论)
  8. 速达3000 数据库备份文件分析
  9. 用overleaf 写 计算机学报 格式的论文
  10. 用c语言软件制作监察病毒,弄了个病毒。用的是C语言,大家看看!