Code Description
0 (0x0000) Unknown
1 (0x0001) PCM/uncompressed
2 (0x0002) Microsoft ADPCM
6 (0x0006) ITU G.711 a-law
7 (0x0007) ITU G.711 µ-law
17 (0x0011) IMA ADPCM
20 (0x0016) ITU G.723 ADPCM (Yamaha)
49 (0x0031) GSM 6.10
64 (0x0040) ITU G.721 ADPCM
80 (0x0050) MPEG
65,536 (0xFFFF) Experimental
其中:
6. ITU G.711 a-law [3]
G.711标准也是PCM码的一种。是国际电报联盟(International Telegraph Union, ITU)订定出来的一套语音压缩标准,主要用于电话。它主要用脉冲编码调制对音频采样,采样率为8kHz。它利用一个 64kbit/s 未压缩通道传输语音讯号。起压缩率为1:2,即把16位数据压缩成8位。G.711是主流的波形声音编解码器。
G.711 标准下主要有两种压缩算法。一种是?μ-law algorithm (又称μ-law),主要运用于北美和日本;另一种就是a-law algorithm,主要运用于欧洲和世界其他地区。其中,a-law是特别设计用来方便计算机处理的。
7. ITU G.711 µ-law
G.711标准下的另一种压缩算法,主要运用于北美和日本,美国电话格式(CCITT G.711)就采用这种算法。
Android 手机录音的码流就是16位PCM 编码的,可以直接和wav解码出来的码流进行混音处理,下面是JNI 的C代码实现:
- /*
- * 文件名:wav_codec.c
- * 创建时间:2014年11月26日
- */
- #include <wav_codec.h>
- #define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
- #define QUANT_MASK (0xf) /* Quantization field mask. */
- #define SEG_SHIFT (4) /* Left shift for segment number. */
- #define SEG_MASK (0x70) /* Segment field mask. */
- #define BIAS (0x84) /* Bias for linear code. */
- #define CLIP 8159
- static const short seg_aend[8] = { 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF,
- 0xFFF };
- static const short seg_uend[8] = { 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF,
- 0x1FFF };
- short search(short val, short *table, short size);
- char linear2alaw(short pcm_val);
- short alaw2linear(unsigned char a_val);
- char linear2ulaw(short pcm_val);
- short ulaw2linear(unsigned char u_val);
- short PcmMix0(short shortIntCh0, short shortIntCh1);
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: pcmToAlaw
- * Signature: ([BI)[B
- * 16位pcm转a-law
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_PcmToAlaw(
- JNIEnv *env, jclass thiz, jbyteArray src) {
- int length = (*env)->GetArrayLength(env, src);
- uint8 buffer[length];
- (*env)->GetByteArrayRegion(env, src, 0, length, buffer);
- char resultTemp[length / 2];
- int i = 0;
- for (i = 0; i < length; i += 2) {
- short shortInt = (((buffer[i + 1] & 0xff) << 8) | (buffer[i] & 0x00ff));
- resultTemp[i / 2] = linear2alaw(shortInt);
- }
- jbyteArray result = (*env)->NewByteArray(env, length / 2);
- (*env)->SetByteArrayRegion(env, result, 0, length / 2, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: AlawTopcm
- * Signature: ([BI)[B
- * a-law转16位pcm
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_AlawToPcm(
- JNIEnv *env, jclass thiz, jbyteArray src) {
- int length = (*env)->GetArrayLength(env, src);
- uint8 buffer[length];
- (*env)->GetByteArrayRegion(env, src, 0, length, buffer);
- char resultTemp[length * 2];
- int i = 0;
- for (i = 0; i < length; i++) {
- short resultShort = alaw2linear(buffer[i]);
- resultTemp[i * 2] = (char) (resultShort);
- resultTemp[i * 2 + 1] = (char) (resultShort >> 8);
- }
- jbyteArray result = (*env)->NewByteArray(env, length * 2);
- (*env)->SetByteArrayRegion(env, result, 0, length * 2, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: pcmToUlaw
- * Signature: ([BI)[B
- * 16位pcm转u-law
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_PcmToUlaw(
- JNIEnv *env, jclass thiz, jbyteArray src) {
- int length = (*env)->GetArrayLength(env, src);
- uint8 buffer[length];
- (*env)->GetByteArrayRegion(env, src, 0, length, buffer);
- char resultTemp[length / 2];
- int i = 0;
- for (i = 0; i < length; i += 2) {
- short shortInt = (((buffer[1 + i] & 0xff) << 8) | (buffer[i] & 0x00ff));
- resultTemp[i / 2] = linear2ulaw(shortInt);
- }
- jbyteArray result = (*env)->NewByteArray(env, length / 2);
- (*env)->SetByteArrayRegion(env, result, 0, length / 2, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: UlawTopcm
- * Signature: ([BI)[B
- * u-law转16位pcm
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_UlawToPcm(
- JNIEnv *env, jclass thiz, jbyteArray src) {
- int length = (*env)->GetArrayLength(env, src);
- uint8 buffer[length];
- (*env)->GetByteArrayRegion(env, src, 0, length, buffer);
- char resultTemp[length * 2];
- int i = 0;
- for (i = 0; i < length; i++) {
- short resultShort = ulaw2linear(buffer[i]);
- resultTemp[i * 2] = (char) (resultShort);
- resultTemp[i * 2 + 1] = (char) (resultShort >> 8);
- }
- jbyteArray result = (*env)->NewByteArray(env, length * 2);
- (*env)->SetByteArrayRegion(env, result, 0, length * 2, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: Pcm16ALawCompound
- * Signature: ([BI)[B
- * 16位pcm与a-law混音成a-law
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_Pcm16ALawCompoundAlaw(
- JNIEnv *env, jclass thiz, jbyteArray ch0, jbyteArray ch1) {
- int ch0Length = (*env)->GetArrayLength(env, ch0);
- int ch1Length = (*env)->GetArrayLength(env, ch1);
- uint8 ch0Buffer[ch0Length];
- uint8 ch1Buffer[ch1Length];
- (*env)->GetByteArrayRegion(env, ch0, 0, ch0Length, ch0Buffer);
- (*env)->GetByteArrayRegion(env, ch1, 0, ch1Length, ch1Buffer);
- char alawPcm[ch1Length * 2];
- int i = 0;
- for (i = 0; i < ch1Length; i++) {
- short resultShort = alaw2linear(ch1Buffer[i]);
- alawPcm[i * 2] = (char) (resultShort);
- alawPcm[i * 2 + 1] = (char) (resultShort >> 8);
- }
- char compoundTemp[ch0Length];
- for (i = 0; i < ch0Length; i += 2) {
- //转换结果
- short dataMix = 0;
- //byte2short
- short shortIntCh0 = (((ch0Buffer[1 + i] & 0xff) << 8)
- | (ch0Buffer[i] & 0x00ff));
- short shortIntCh1 = (((alawPcm[1 + i] & 0xff) << 8)
- | (alawPcm[i] & 0x00ff));
- dataMix = PcmMix0(shortIntCh0, shortIntCh1);
- compoundTemp[i] = (char) (dataMix);
- compoundTemp[i + 1] = (char) (dataMix >> 8);
- }
- char resultTemp[ch1Length];
- for (i = 0; i < ch0Length; i += 2) {
- short shortInt = (((compoundTemp[i + 1] & 0xff) << 8)
- | (compoundTemp[i] & 0x00ff));
- resultTemp[i / 2] = linear2alaw(shortInt);
- }
- jbyteArray result = (*env)->NewByteArray(env, ch1Length);
- (*env)->SetByteArrayRegion(env, result, 0, ch1Length, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: Pcm16ULawCompound
- * Signature: ([BI)[B
- * 16位pcm与u-law混音成a-law
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_Pcm16ULawCompoundAlaw(
- JNIEnv *env, jclass thiz, jbyteArray ch0, jbyteArray ch1) {
- int ch0Length = (*env)->GetArrayLength(env, ch0);
- int ch1Length = (*env)->GetArrayLength(env, ch1);
- uint8 ch0Buffer[ch0Length];
- uint8 ch1Buffer[ch1Length];
- (*env)->GetByteArrayRegion(env, ch0, 0, ch0Length, ch0Buffer);
- (*env)->GetByteArrayRegion(env, ch1, 0, ch1Length, ch1Buffer);
- char ulawPcm[ch1Length * 2];
- int i = 0;
- for (i = 0; i < ch1Length; i++) {
- short resultShort = ulaw2linear(ch1Buffer[i]);
- ulawPcm[i * 2] = (char) (resultShort);
- ulawPcm[i * 2 + 1] = (char) (resultShort >> 8);
- }
- char compoundTemp[ch0Length];
- for (i = 0; i < ch0Length; i += 2) {
- //转换结果
- short dataMix = 0;
- //byte2short
- short shortIntCh0 = (((ch0Buffer[1 + i] & 0xff) << 8)
- | (ch0Buffer[i] & 0x00ff));
- short shortIntCh1 = (((ulawPcm[1 + i] & 0xff) << 8)
- | (ulawPcm[i] & 0x00ff));
- dataMix = PcmMix0(shortIntCh0, shortIntCh1);
- compoundTemp[i] = (char) (dataMix);
- compoundTemp[i + 1] = (char) (dataMix >> 8);
- }
- char resultTemp[ch1Length];
- for (i = 0; i < ch0Length; i += 2) {
- short shortInt = (((compoundTemp[i + 1] & 0xff) << 8)
- | (compoundTemp[i] & 0x00ff));
- resultTemp[i / 2] = linear2alaw(shortInt);
- }
- jbyteArray result = (*env)->NewByteArray(env, ch1Length);
- (*env)->SetByteArrayRegion(env, result, 0, ch1Length, resultTemp);
- return result;
- }
- /*
- * Class: com_demo_mymix_WavConvertUtils
- * Method: DoublePcm16Compound
- * Signature: ([BI)[B
- * 两路16位PCM编码混音
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_DoublePcm16Compound(
- JNIEnv *env, jclass thiz, jbyteArray ch0, jbyteArray ch1) {
- int ch0Length = (*env)->GetArrayLength(env, ch0);
- int ch1Length = (*env)->GetArrayLength(env, ch1);
- uint8 ch0Buffer[ch0Length];
- uint8 ch1Buffer[ch1Length];
- (*env)->GetByteArrayRegion(env, ch0, 0, ch0Length, ch0Buffer);
- (*env)->GetByteArrayRegion(env, ch1, 0, ch1Length, ch1Buffer);
- char resultTemp[ch1Length];
- int i = 0;
- for (i = 0; i < ch1Length; i += 2) {
- //转换结果
- short dataMix = 0;
- //byte2short
- short shortIntCh0 = (((ch0Buffer[1 + i] & 0xff) << 8)
- | (ch0Buffer[i] & 0x00ff));
- short shortIntCh1 = (((ch1Buffer[1 + i] & 0xff) << 8)
- | (ch1Buffer[i] & 0x00ff));
- dataMix = PcmMix0(shortIntCh0, shortIntCh1);
- resultTemp[i] = (char) (dataMix);
- resultTemp[i + 1] = (char) (dataMix >> 8);
- }
- jbyteArray result = (*env)->NewByteArray(env, ch1Length);
- (*env)->SetByteArrayRegion(env, result, 0, ch1Length, resultTemp);
- return result;
- }
- /*************** 检查输入信号在哪个压缩区间 *********************/
- short search(short val, short *table, short size) {
- short i;
- for (i = 0; i < size; i++) {
- if (val <= *table++) //找出一个与输入信号最相近的值(大于)
- {
- return (i);
- }
- }
- return (size);
- }
- /********************* PCM 2 a-law ****************************/
- char linear2alaw(short pcm_val) {
- short mask;
- short seg;
- unsigned char aval;
- pcm_val = pcm_val >> 3;
- if (pcm_val >= 0) {
- mask = 0xD5; /* sign (7th) bit = 1 */
- } else {
- mask = 0x55; /* sign bit = 0 */
- pcm_val = -pcm_val - 1;
- }
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, (short *) seg_aend, (short) 8);
- /* Combine the sign, segment, and quantization bits. */
- if (seg >= 8) /* out of range, return maximum value. */
- {
- return (unsigned char) (0x7F ^ mask);
- } else {
- aval = (unsigned char) seg << SEG_SHIFT;
- if (seg < 2) {
- aval |= (pcm_val >> 1) & QUANT_MASK;
- } else {
- aval |= (pcm_val >> seg) & QUANT_MASK;
- }
- return (aval ^ mask);
- }
- }
- /***************** a-law 2 PCM *******************/
- short alaw2linear(unsigned char a_val) {
- short t;
- short seg;
- a_val ^= 0x55;
- t = (a_val & QUANT_MASK) << 4;
- seg = ((unsigned) a_val & SEG_MASK) >> SEG_SHIFT;
- switch (seg) {
- case 0:
- t += 8;
- break;
- case 1:
- t += 0x108;
- break;
- default:
- t += 0x108;
- t <<= seg - 1;
- break;
- }
- return ((a_val & SIGN_BIT) ? t : -t);
- }
- /*********************** pcm 2 u law *************************/
- char linear2ulaw(short pcm_val) {
- short mask;
- short seg;
- unsigned char uval;
- /* Get the sign and the magnitude of the value. */
- pcm_val = pcm_val >> 2;
- if (pcm_val < 0) {
- pcm_val = -pcm_val;
- mask = 0x7F;
- } else {
- mask = 0xFF;
- }
- if (pcm_val > CLIP) {
- pcm_val = CLIP; /* clip the magnitude */
- }
- pcm_val += (BIAS >> 2);
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, (short *) seg_uend, (short) 8);
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- if (seg >= 8) /* out of range, return maximum value. */
- {
- return (unsigned char) (0x7F ^ mask);
- } else {
- uval = (unsigned char) (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
- return (uval ^ mask);
- }
- }
- /*********************** ulaw 2 pcm *************************/
- short ulaw2linear(unsigned char u_val) {
- short t;
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & QUANT_MASK) << 3) + BIAS;
- t <<= ((unsigned) u_val & SEG_MASK) >> SEG_SHIFT;
- return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
- }
下面是C头文件:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- #include <android/log.h>
- /* Header for class com_demo_mymix_WavConvertUtils */
- #ifndef LOG_TAG
- #define LOG_TAG "XIAXC"
- #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
- #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
- #endif
- typedef unsigned char uint8;
- typedef unsigned short uint16;
- typedef unsigned int uint32;
- #ifndef _Included_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- #define _Included_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: PcmToAlaw
- * Signature: ([B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_PcmToAlaw
- (JNIEnv *, jclass, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: AlawToPcm
- * Signature: ([B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_AlawToPcm
- (JNIEnv *, jclass, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: PcmToUlaw
- * Signature: ([B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_PcmToUlaw
- (JNIEnv *, jclass, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: UlawToPcm
- * Signature: ([B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_UlawToPcm
- (JNIEnv *, jclass, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: DoublePcm16Compound
- * Signature: ([B[B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_DoublePcm16Compound
- (JNIEnv *, jclass, jbyteArray, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: Pcm16ALawCompoundAlaw
- * Signature: ([B[B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_Pcm16ALawCompoundAlaw
- (JNIEnv *, jclass, jbyteArray, jbyteArray);
- /*
- * Class: cn_easier_RingtoneHelper_logic_diy_WavConvertUtils
- * Method: Pcm16ULawCompoundAlaw
- * Signature: ([B[B)[B
- */
- JNIEXPORT jbyteArray JNICALL Java_cn_easier_RingtoneHelper_logic_diy_WavConvertUtils_Pcm16ULawCompoundAlaw
- (JNIEnv *, jclass, jbyteArray, jbyteArray);
- #ifdef __cplusplus
- }
- #endif
- #endif
一个PCM音频转换与混音的示例
格式转换使用了ACM,参考了《 VC下调用ACM音频编程接口压缩Wave音频》(原文不知何处,GOOGLE一下一大把),混音则采用最简便的线性累加的方法进行累加。
我提供了两个文件A1.PCM和A2.PCM,点击Convert按钮会将这两个文件转换为B1.PCM与B2.PCM,生成这两个文件后,点击MIX按钮会将B1.PCM与B2.PCM混合并生成M.PCM。
A1.PCM、A2.PCM、M.PCM格式均为 8Bit 8000Sample 1Channel PCM A-Law格式
B1.PCM、B2.PCM格式为16Bit 8000Sample 1Channel PCM Line格式
以上文件可以使用CoolEdit或Adobe Audition打开。
工程基于CodeGear C++ Builder 2007,在Windows Server 2003和Vista下均调试通过。
代码下载: 点此下载