C语言中如何将存储PCM数据的int*数组转换为short*数组
C语言下16位PCM数据 int数组转short实现
先修正现有代码的易踩坑点
- 你当前
malloc的尺寸逻辑存在冗余:data_size一般指代WAV文件中PCM段的实际字节长度,16位位深下单采样占2字节,你直接将data_size * sizeof(int)作为分配长度,会多分配2倍内存。虽然只要readPCM按实际数据长度写入不会直接崩溃,但后续转换时必须严格以data_size作为有效数据的长度依据,不能用分配的总内存长度计算采样数。
分场景实现方案
因为int*存储PCM的常见逻辑有两种,对应不同的转换写法:
场景1:readPCM接口将每个16位采样值直接存入int数组元素
这是音频读取接口最常用的封装逻辑:接口传入int*缓冲区时,会把对应位深的采样值(16位下范围是-32768 ~ 32767)存在每个int元素的低16位,高16位为符号扩展或无效填充。
这种场景下的转换代码:
#include <stdlib.h> // 计算有效采样数:16位每采样占2字节 const long n_samples = data_size / 2; short *pcm_short = (short*)malloc(n_samples * sizeof(short)); if (pcm_short == NULL) { // 内存分配失败,释放原有缓冲区后退出 free(data); exit(EXIT_FAILURE); } // 逐元素转换 for (long i = 0; i < n_samples; i++) { int sample_val = data[i]; // 边界钳位,避免值超出short的有效范围(和Java中short的范围一致) sample_val = sample_val > 32767 ? 32767 : sample_val; sample_val = sample_val < -32768 ? -32768 : sample_val; pcm_short[i] = (short)sample_val; }
说明:如果确认
readPCM写入的int值本身就没有超出16位范围,边界钳位的代码可以省略,直接强转赋值即可。
场景2:readPCM接口将原始PCM字节流直接写入int*内存
这种场景和你参考的Java逻辑完全对应:int*指向的内存本质是连续的PCM原始字节,没有按采样拆分,需要从连续字节中拼接出16位short值。
这种场景下的转换代码:
#include <stdlib.h> #include <stdio.h> // 计算有效采样数 const long n_samples = data_size / 2; short *pcm_short = (short*)malloc(n_samples * sizeof(short)); if (pcm_short == NULL) { free(data); exit(EXIT_FAILURE); } // 将int*指针转为字节指针,和Java中byte[]输入的逻辑对齐 unsigned char *raw_pcm_bytes = (unsigned char*)data; for (long i = 0; i < n_samples; i++) { // 按WAV文件默认的小端序拼接采样:低字节在前,高字节在后 unsigned char byte_low = raw_pcm_bytes[2 * i]; unsigned char byte_high = raw_pcm_bytes[2 * i + 1]; // 拼接为有符号short值 pcm_short[i] = (short)(byte_low | (byte_high << 8)); // 如果处理的是大端序PCM,替换为下面的拼接逻辑即可,和你Java参考的实现逻辑完全一致 // pcm_short[i] = (short)(byte_high | (byte_low << 8)); }
通用注意事项
- 转换完成后及时释放原int数组的内存,避免泄漏:
free(data); data = NULL; - 不要直接将int指针强行强转为short使用,除非完全确认内存布局是连续的16位采样,否则会出现字节错位、采样值完全错乱的问题。
- 所有循环的计数长度严格按
data_size计算,不要用之前malloc的总内存长度作为依据,避免读取到未初始化的野值。
内容的提问来源于stack exchange,提问作者Subbir Rahman
相关产品推荐
相关产品推荐

