- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
ADPCM(Adaptive Differential Pulse Code Modulation, 自适应差分脉冲编码调制) 是一种音频信号数字化编码技术, 音频压缩标准G.722, G.723, G.726 中都会使用到 ADPCM 。
G.722 is an ITU-T standard 7 kHz wideband audio codec operating at 48, 56 and 64 kbit/s. It was approved by ITU-T in November 1988. Technology of the codec is based on sub-band ADPCM (SB-ADPCM). The corresponding narrow-band codec based on the same technology is G.726.[1] 。
G.723 is an ITU-T standard speech codec using extensions of G.721 providing voice quality covering 300 Hz to 3400 Hz using Adaptive Differential Pulse Code Modulation (ADPCM) to 24 and 40 kbit/s for digital circuit multiplication equipment (DCME) applications. 。
G.726 is an ITU-T ADPCM speech codec standard covering the transmission of voice at rates of 16, 24, 32, and 40 kbit/s. It was introduced to supersede both G.721, which covered ADPCM at 32 kbit/s, and G.723, which described ADPCM for 24 and 40 kbit/s. G.726 also introduced a new 16 kbit/s rate. The four bit rates associated with G.726 are often referred to by the bit size of a sample, which are 2, 3, 4, and 5-bits respectively. The corresponding wide-band codec based on the same technology is G.722. 。
要了解ADPCM, 需要先了解PCM和DPCM. 。
PCM 是声音模拟信号数字化的一种基础技术, 就是把时间连续取值连续的模拟信号变换成离散取值的数字信号, 熟悉ADC(模拟数字转换)的应该很好理解, 过程就是采样, 量化和编码. 。
用固定的频率, 对模拟信号提取样本值, 人耳能够感觉到的最高频率为20kHz, 根据 奈奎斯特采样定律, Nyquist rate , 只需要每秒进行40k次采样, 就能覆盖人耳的听觉范围, 也就是说采样高于40k每秒对于普通人来说, 听觉基本上没有提升了. 。
In signal processing, the Nyquist rate, named after Harry Nyquist, is a value (in units of samples per second or hertz, Hz) equal to twice the highest frequency (bandwidth) of a given function or signal. When the function is digitized at a higher sample rate (see § Critical frequency), the resulting discrete-time sequence is said to be free of the distortion known as aliasing. Conversely, for a given sample-rate the corresponding Nyquist frequency in Hz is one-half the sample-rate. Note that the Nyquist rate is a property of a continuous-time signal, whereas Nyquist frequency is a property of a discrete-time system. 。
人类语音的频率在300 - 3000 Hz之间, 成年男性的语音基频在85-155Hz, 女性在165-255Hz, 同时会产生丰富的谐振频率, 在通信上, 需要保证 300 - 3400Hz 的频率范围才能满足正常通话, 对于这个频率范围, 需要使用 8K每秒的采样率. 通常电话的采样率为8k和16kHz. 。
The voiced speech of a typical adult male will have a fundamental frequency from 85 to 155 Hz, and that of a typical adult female from 165 to 255 Hz.[3] Thus, the fundamental frequency of most speech falls below the bottom of the voice frequency band as defined. However, enough of the harmonic series will be present for the missing fundamental to create the impression of hearing the fundamental tone. 。
日常音频信号常见的采样率为8K, 16k, 22.05k, 32k, 44.1k, 48k, 192k. 常见的无线电广播最高采样率为22.05K, CD最高采样率为44.1k, DVD最高采样率为48k, Hi-Res音频采样率可以高达192k. 。
量化和编码就是把采样得到的信号幅度转换成数字值(ADC), 再组织成固定尺寸的序列. PCM实际上就是一个大数组, 数组中每个值, 代表了当前时间点上的模拟量强度, 在播放时在对应的时间点上被转换为模拟量输出(DAC). 。
在量化的过程中会产生误差, 一般而言, ADC的精度越高, 失真越小. 常见的量化位数为8bit, 16bit, 24bit. 模数转换都会有误差和底噪, 对于ADC而言, 除了精度, 还有转换的实现方式, 电压基准, 电磁环境等都会对转换效果造成影响. 。
PCM 保存的是最原始的模数转换结果, 是不压缩的, 数据量比较大, 存储和通讯都会占用很大资源, 需要将数据压缩以减少通信带宽和存储的资源消耗. 。
将音频PCM的数组展开观察可以看到, 数据值与相邻的值通常是比较连续的, 不会突然很高或者突然很低, 两点之间差值不会太大, 所以这个差值可以用很少的几个位(比如4bit)表示. 这样只需要知道起始点的值和每个点的差值, 就可以还原得到原来的序列. 记录的差值序列就是DPCM数据, 这样数据量会小很多. 。
以8k采样率为例, 如果量化精度为16bit, 则1秒的数据量为8000 * 16 bit = 128kb, 如果用4bit的表示差值, 则1秒的 PCM 数据转成 DPCM 只需要约 32kb. 。
DPCM存在一个问题, 音频信号虽然比较连续性, 但是存在差值较大的情况, 例如差值超过4bit表示的范围(-15, 15) 就无法很好还原原来的PCM序列, 这时候如果增大差值宽度, 例如用6bit, 8bit表示, 可以减小这个问题, 但数据量也增大了. 。
ADPCM 的出发点就是解决 DPCM 的差值宽度问题, 通过定义一个差值表(例如IMA ADPCM 中使用 89个固定差值, 取值从7到32767), 将差值的范围放宽到16bit, 此时差值在数组中的编号只需要6bit就可以表示(0 - 88), 再进一步只记录编号的变化值, 就将变化量压缩到了4bit. 。
ADPCM算法是一个统称, 有 YAMAHA, Microsoft, IMA 等标准, 下面以嵌入式开发中最常见的 IMA ADPCM 为例进行说明 。
在了解编解码算法前, 先了解 IMA ADPCM 的编码格式. 。
16bit的 IMA ADPCM 编码产生的数据为一个数组, 数组中每个数都是4个bit(值范围为0x00到0x0F), 因为C语言编程中变量的最小单位是byte, 所以通常表示为 uint8_t 数组, 数组中每个元素存储2个 ADPCM 编码值, 或者对于32位系统使用 uint32_t, 每个元素存储8个 ADPCM 编码值. 。
对于IMA ADPCM, 还需要了解两个码表, 一个是 差值步长码表 , 一个是 差值步长下标变化量码表 。
知道了 ADPCM 编码值的格式, 也知道了两个码表, 就可以了解 ADPCM 编码值中各个bit位的作用. 。
例如一个编码值为 0x05 , 对应二进制 0101 , 其中最高位为0, 代表变化为正, 输出值是在前一个值上叠加; 低三位为5, 代表差值步长下标变化量为+4, 也就是差值步长变大了, 另外第三位的每一位分别代表对应实际差值的差值步长的倍数, 参与了差值的计算 。
bit位 | 值 | 含义 |
---|---|---|
4 | 0 | 最高位代表了正负, 如果是0, 代表这个差值是正的, 1则表示差值是负的 |
3 | 1 | 1-3bit合起来代表了下一个值的差值步长下标变化量, 同时每个bit代表当前步长的一个系数, 这个bit表示1倍差值步长 |
2 | 0 | 这个bit表示0.5倍差值步长 |
1 | 1 | 这个bit表示0.25倍差值步长 |
以上会产生 1 + 0 + 0.25 = 1.25倍的差值步长, 加上固定的1/8步长, 就是说这一步产生的输出 = 前一步数值 + 当前差值步长 * 1.375, 这个值会作为下一步的数值, 同时下一步的差值步长下标+4, 也就是下一个值的计算中用到的差值步长增大了. 。
输入序列为 。
uint16_t nums[12] = {0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0050, 0x0050, 0x0040, 0x0400, 0x0400, 0x0400, 0x0400};
使用 IMA ADPCM 对上面的序列进行编码, 每一步的记录, 第一列为输入, 最后一列为输出 。
解码输出的序列为 。
# 解码结果
0x000B, 0x0021, 0x002E, 0x003F, 0x004E, 0x0050, 0x0052, 0x0040, 0x0066, 0x00B9, 0x016E, 0x02F5
# 作为参照的原输入序列
0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0050, 0x0050, 0x0040, 0x0400, 0x0400, 0x0400, 0x0400
通过观察可以得到几个规律 。
/* 差值步长码表 */
const uint16_t StepSizeTable[89]={7,8,9,10,11,12,13,14,16,17,
19,21,23,25,28,31,34,37,41,45,
50,55,60,66,73,80,88,97,107,118,
130,143,157,173,190,209,230,253,279,307,
337,371,408,449,494,544,598,658,724,796,
876,963,1060,1166,1282,1411,1552,1707,1878,2066,
2272,2499,2749,3024,3327,3660,4026,4428,4871,5358,
5894,6484,7132,7845,8630,9493,10442,11487,12635,13899,
15289,16818,18500,20350,22385,24623,27086,29794,32767};
/* 差值步长下标变化量码表 */
const int8_t IndexTable[16]={0xff,0xff,0xff,0xff,2,4,6,8,0xff,0xff,0xff,0xff,2,4,6,8};
熟悉前面的格式和编解码逻辑, 下面的代码就比较好理解了. 函数输入是一个16bit数字, 输出一个4bit数字, 中间用两个static变量, 用于存储前一步确定的差值步长下标, 以及前一次的解码值, 参与下一个值的编码计算 。
uint8_t ADPCM_Encode(int32_t sample)
{
// index 存储的是上一次预测的差值步长下标, 通过查表可以得到步长
static int16_t index = 0;
// predsample 存储的是上一个解码值, 解码还原时产生的就是这个值
static int32_t predsample = 0;
// 当前输入值, 编码后的输出, 4个bit
uint8_t code=0;
uint16_t tmpstep=0;
int32_t diff=0;
int32_t diffq=0;
uint16_t step=0;
// 先拿到差值步长
step = StepSizeTable[index];
// 看看当前输入值, 跟上一个输出值的差值
diff = sample-predsample;
// 如果是负的, 就给code 的高4位置1, 表示差值是负数
if (diff < 0)
{
code=8;
diff = -diff;
}
tmpstep = step;
// 以下根据差值, 计算步长的乘数系数(同时就会产生步长下标的偏移量)
// 首先是固定的 1/8个step
diffq = (step >> 3);
// 下面就是按位进行除法, 每一位的结果被依次赋值到code的3,2,1位, 同时presample的值也算出来了
if (diff >= tmpstep)
{
code |= 0x04;
diff -= tmpstep;
diffq += step;
}
tmpstep = tmpstep >> 1;
if (diff >= tmpstep)
{
code |= 0x02;
diff -= tmpstep;
diffq+=(step >> 1);
}
tmpstep = tmpstep >> 1;
if (diff >= tmpstep)
{
code |=0x01;
diffq+=(step >> 2);
}
// 到这一步, 如果code值大于等于4或小于等于-4, 就说明差值大于当前的步长(至少是1.125倍), 步长要增加, 否则步长要收缩
// 以下都是避免值越界的一些计算
if (code & 8)
{
predsample -= diffq;
}
else
{
predsample += diffq;
}
if (predsample > 32767)
{
predsample = 32767;
}
else if (predsample < -32768)
{
predsample = -32768;
}
// 查表得到下一个数的差值步长下标
index += IndexTable[code];
// 避免越界
if (index <0)
{
index = 0;
}
else if (index > 88)
{
index = 88;
}
// 避免越界
return (code & 0x0f);
}
解码就是将ADPCM数组中的每个4bit数值, 还原回编码过程中的每个presample值. 。
/**
* @brief ADPCM_Decode.
* @param code: a byte containing a 4-bit ADPCM sample.
* @retval : 16-bit ADPCM sample
*/
int16_t ADPCM_Decode(uint8_t code)
{
// 上一步预测的差值步长下标
static int16_t index = 0;
// 上一步的解码值
static int32_t predsample = 0;
uint16_t step=0;
int32_t diffq=0;
// 得到当前步长
step = StepSizeTable[index];
// 根据步长和4bit编码值, 计算当前的实际差值
// 先是1/8的固定差值
diffq = step>> 3;
// 第3位, 1倍步长
if (code&4)
{
diffq += step;
}
// 第2位, 0.5倍步长
if (code&2)
{
diffq += step>>1;
}
// 第1位, 0.25倍步长
if (code&1)
{
diffq += step>>2;
}
// 根据正负符号, 加或者减, 算出解码结果
if (code&8)
{
predsample -= diffq;
}
else
{
predsample += diffq;
}
// 防止越界
if (predsample > 32767)
{
predsample = 32767;
}
else if (predsample < -32768)
{
predsample = -32768;
}
// 查表, 计算得到下一个差值步长的下标
index += IndexTable [code];
// 防止越界
if (index < 0)
{
index = 0;
}
if (index > 88)
{
index = 88;
}
// 返回解码结果
return ((int16_t)predsample);
}
以上说明了ADPCM的原理, 以及通过对 IMA ADPCM 编码代码的分析, 了解 ADPCM 的具体实现. ADPCM的优点是计算简单, 对CPU和存储资源的损耗很小, 以及可观的压缩比(接近4:1), 编解码延时最短(相对其它技术), 缺点是有损压缩, 音质不算好, 压缩率也不算高. 因为对硬件要求低, 适用于低成本低功耗的嵌入式硬件. 对于资源不受限, 有高音质需求, 或者有更高压缩比需求的场景, 可以使用其它的编码算法, 例如LPCM, AAC, MP3等. 。
最后此篇关于ADPCM(自适应差分脉冲编码调制)的原理和计算的文章就讲到这里了,如果你想了解更多关于ADPCM(自适应差分脉冲编码调制)的原理和计算的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如果之前已经回答过这个问题,我深表歉意,但我无法找到我想要的东西。 我有一个 Box2D 动态主体,我对其应用线性脉冲以将其变成射弹。因此,当我单击屏幕上的任意位置时,我希望 body 投影到触摸位置
脉冲神经网络和循环神经网络都可以对时变信息进行建模。但我不确定哪种模型相对于计算成本更好。使用更复杂的脉冲神经网络是否值得,或者循环神经网络是否可以在所需计算量少得多的情况下工作?脉冲网络收敛得更快吗
如何在我的应用程序运行且屏幕打开时使 LED 或轨迹球闪烁或闪烁?比如接到电话时? 谢谢 最佳答案 Android 具有仅在屏幕关闭时 LED 才会亮起的硬编码功能。这是在源代码 Notificati
我的目标是从健身手环获取数据(脉搏)Torntisc T1使用我的应用程序并独立处理来自手环的数据。 为了实现,我使用了 Xamarin 并找到了一个 Bluetooth LE plugin for
我很难理解 Wait()、Pulse()、PulseAll()。他们都能避免僵局吗?如果您解释一下如何使用它们,我将不胜感激? 最佳答案 简短版: lock(obj) {...} 是 Monitor.
我在 Cigarette Smoker Problem 工作. 我只应该使用 Monitor 类。没有信号/信号量。 (是的,这是给学校的,但不是家庭作业,只是我的实践测试的免费练习,我真的需要做好准
我想使用 bcm2835.h 和纯 C 语言通过 PWM 控制 LED 二极管。我的代码不起作用。我错过了什么? 我尝试过“gpio”控制台命令,它工作正常,所以我知道 LED 已连接到正确的端口。我
在 ARKit/SceneKit 中,当用户点击按钮时,我想对我的节点施加一个脉冲。我希望冲动来自当前用户的角度。这意味着节点将远离用户的视角。多亏了这段代码,我能够获得当前的方向/方向: func
我正试图在 SK/SWIFT 中“脉冲”一个 Sprite 。我尝试使用 For 循环和 .setScale 进行粗略尝试,但它们不起作用(没有错误 - 只是没有动画)。我觉得使用 SKActions
我想为以下绘图符号设置动画(脉冲、发光)。实现所需行为的最佳方法是什么。谢谢 最佳答案 您不能为绘图符号设置动画。您可以做的是在突出显示的点上放置一个绘图空间注释。创建一个自定义的 CPTLayer
在 Perl 下,在 Linux 上将 Serial::Device 作为文件打开会重置我的 Arduino,但我不希望它被重置。 Arduino 可以通过脉冲 DTR 来重置,因此默认打开串口设备必
我用 alsa 成功渲染了音频,但是我完全无法确定 channel 映射。正如各种消息来源所说,我调用 snd_pcm_get_chmap设置好硬件和软件参数并准备好设备后。 snd_config_g
我是一名优秀的程序员,十分优秀!