app-starlock/lib/tools/G711Tool.dart
2025-04-02 17:36:27 +08:00

274 lines
7.0 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* G711Tool - G.711音频编解码工具类
*
* G.711是一种用于音频压缩的ITU-T标准主要用于电话系统。
* 它有两种主要变体A-law主要用于欧洲和国际电话系统和μ-law主要用于北美和日本
*
* 该类提供了PCM线性音频数据与G.711 A-law/μ-law格式之间的转换功能。
* 编码过程将16位线性PCM样本压缩为8位解码过程则相反。
*/
class G711Tool {
// 常量定义
static const int SIGN_BIT = 0x80; // A-law字节的符号位
static const int QUANT_MASK = 0xf; // 量化字段掩码
static const int NSEGS = 8; // A-law段数
static const int SEG_SHIFT = 4; // 段号左移位数
static const int SEG_MASK = 0x70; // 段字段掩码
static const int BIAS = 0x84; // 线性编码的偏置值
// 查找表
/**
* μ-law到A-law的转换表
* 用于在两种编码格式之间进行转换而不需要先解码为线性PCM
*/
static final List<int> _u2a = [
// u- to A-law conversions
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 27, 29, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
128
];
/**
* A-law到μ-law的转换表
* 用于在两种编码格式之间进行转换而不需要先解码为线性PCM
*/
static final List<int> _a2u = [
// A- to u-law conversions
1, 3, 5, 7, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, 49, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64,
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
127
];
/**
* 段结束值表
* 用于确定PCM值应该映射到哪个段
* 每个段代表不同的量化步长
*/
static final List<int> _segEnd = [
0xFF,
0x1FF,
0x3FF,
0x7FF,
0xFFF,
0x1FFF,
0x3FFF,
0x7FFF
];
/**
* 在表中搜索值所属的段
*
* @param val 要搜索的值
* @param table 段结束值表
* @param size 表大小
* @return 段索引
*/
static int _search(int val, List<int> table, int size) {
for (int i = 0; i < size; i++) {
if (val <= table[i]) return i;
}
return size;
}
/**
* 将线性PCM值转换为A-law编码值
*
* 转换步骤:
* 1. 确定符号位和掩码
* 2. 对负值进行特殊处理
* 3. 确定段号
* 4. 根据段号计算A-law值
*
* @param pcmVal 16位线性PCM值
* @return 8位A-law编码值
*/
static int _linear2alaw(int pcmVal) {
int mask;
int seg;
int aval;
if (pcmVal >= 0) {
mask = 0xD5; // sign (7th) bit = 1
} else {
mask = 0x55; // sign bit = 0
pcmVal = -pcmVal - 1;
if (pcmVal < 0) pcmVal = 32767;
}
seg = _search(pcmVal, _segEnd, 8);
if (seg >= 8) return 0x7F ^ mask;
aval = seg << SEG_SHIFT;
if (seg < 2) {
aval |= (pcmVal >> 4) & QUANT_MASK;
} else {
aval |= (pcmVal >> (seg + 3)) & QUANT_MASK;
}
return aval ^ mask;
}
/**
* 将A-law编码值转换为线性PCM值
*
* 转换步骤:
* 1. 异或操作恢复原始位模式
* 2. 提取量化值和段号
* 3. 根据段号计算线性值
* 4. 应用符号位
*
* @param aVal 8位A-law编码值
* @return 16位线性PCM值
*/
static int _alaw2linear(int aVal) {
int t;
int seg;
aVal ^= 0x55;
t = (aVal & QUANT_MASK) << 4;
seg = (aVal & SEG_MASK) >> SEG_SHIFT;
switch (seg) {
case 0:
t += 8;
break;
case 1:
t += 0x108;
break;
default:
t += 0x108;
t <<= seg - 1;
}
return (aVal & SIGN_BIT) != 0 ? t : -t;
}
/**
* 将线性PCM值转换为μ-law编码值
*
* 转换步骤与A-law类似但使用不同的偏置和掩码
*
* @param pcmVal 16位线性PCM值
* @return 8位μ-law编码值
*/
static int _linear2ulaw(int pcmVal) {
int mask;
int seg;
int uval;
if (pcmVal < 0) {
pcmVal = BIAS - pcmVal;
mask = 0x7F;
} else {
pcmVal += BIAS;
mask = 0xFF;
}
seg = _search(pcmVal, _segEnd, 8);
if (seg >= 8) return 0x7F ^ mask;
uval = (seg << 4) | ((pcmVal >> (seg + 3)) & 0xF);
return uval ^ mask;
}
/**
* 将μ-law编码值转换为线性PCM值
*
* @param uVal 8位μ-law编码值
* @return 16位线性PCM值
*/
static int _ulaw2linear(int uVal) {
uVal = ~uVal;
int t = ((uVal & QUANT_MASK) << 3) + BIAS;
t <<= (uVal & SEG_MASK) >> SEG_SHIFT;
return (uVal & SIGN_BIT) != 0 ? (BIAS - t) : (t - BIAS);
}
/**
* 将A-law编码值转换为μ-law编码值
*
* 使用查找表直接转换,避免解码再编码的性能损失
*
* @param aval 8位A-law编码值
* @return 8位μ-law编码值
*/
static int alaw2ulaw(int aval) {
aval &= 0xff;
return (aval & 0x80) != 0
? (0xFF ^ _a2u[aval ^ 0xD5])
: (0x7F ^ _a2u[aval ^ 0x55]);
}
/**
* 将μ-law编码值转换为A-law编码值
*
* 使用查找表直接转换,避免解码再编码的性能损失
*
* @param uval 8位μ-law编码值
* @return 8位A-law编码值
*/
static int ulaw2alaw(int uval) {
uval &= 0xff;
return (uval & 0x80) != 0
? (0xD5 ^ (_u2a[0xFF ^ uval] - 1))
: (0x55 ^ (_u2a[0x7F ^ uval] - 1));
}
/**
* 将PCM数据编码为G.711格式
*
* @param pcm 输入PCM数据
* @param lawFlag 编码类型标志: 0表示A-law, 1表示μ-law
* @return 编码后的G.711数据
*/
static List<int> encode(List<int> pcm, int lawFlag) {
List<int> code = List<int>.filled(pcm.length, 0);
if (lawFlag == 0) {
for (int i = 0; i < pcm.length; i++) {
code[i] = _linear2alaw(pcm[i]);
}
} else {
for (int i = 0; i < pcm.length; i++) {
code[i] = _linear2ulaw(pcm[i]);
}
}
return code;
}
/**
* 将G.711数据解码为PCM格式
*
* @param code 输入G.711数据
* @param lawFlag 编码类型标志: 0表示A-law, 1表示μ-law
* @return 解码后的PCM数据
*/
static List<int> decode(List<int> code, int lawFlag) {
List<int> pcm = List<int>.filled(code.length, 0);
if (lawFlag == 0) {
for (int i = 0; i < code.length; i++) {
pcm[i] = _alaw2linear(code[i]);
}
} else {
for (int i = 0; i < code.length; i++) {
pcm[i] = _ulaw2linear(code[i]);
}
}
return pcm;
}
}