------------------------------------全系列文章目录------------------------------------
-
PCM(Pulse Code Modulation,脉冲编码调制),PCM音频数据是指经过采样、量化、编码转换成的未压缩数字音频数据。
-
WAV最常见的声音文件格式之一,是微软公司专门为Windows开发的一种标准数字音频文件,该文件能记录各种单声道或立体声的声音信息,并能保证声音不失真。
-
WAV格式组成
- RIFF(Resource Interchange File Format,资源交换档案标准),是一种把资料储存在被标记的区块(tagged chunks)中的档案格式(meta-format),不同编码的音频/视频流按照RIFF所定义的存储规则保存,在读取播放时,就可以按照RIFF规则分析文件,正常解析出音频/视频流。
-
示例代码
- 按照上述定义,声明如下数据结构
struct RIFF_t { char ID[4]; uint32_t size; char format[4]; }; struct FMT_t { char ID[4]; uint32_t size; uint16_t audio_format; uint16_t channels; uint32_t sample_rate; uint32_t byte_rate; uint16_t block_align; uint16_t bits_per_sample; }; struct DATA_t { char ID[4]; uint32_t size; //void *data; //此处因为数据需要从文件中边读取边存储,因此为了方便操作,不声明data }; struct PCM_to_WAV_t { char *in_file_name; char *out_file_name; FILE *in_file; FILE *out_file; uint16_t pcm_data; //保存从PCM文件中读取的数据,因为PCM数据大小必然是16的倍数,所以声明为uint16_t struct RIFF_t riff; struct FMT_t fmt; struct DATA_t data; };
- 初始化操作
int PCM_to_WAV_Init(PCM_to_WAV_t *p2w, int argc, char** argv) { if (argc == 3) { p2w->in_file_name = argv[1]; p2w->fmt.sample_rate = 44100; p2w->fmt.channels = 2; p2w->fmt.bits_per_sample = 16; p2w->out_file_name = argv[2]; } else if (argc == 6) { p2w->in_file_name = argv[1]; p2w->fmt.sample_rate = atoi(argv[2]); p2w->fmt.channels = atoi(argv[3]); p2w->fmt.bits_per_sample = atoi(argv[4]); p2w->out_file_name = argv[5]; } else { printf("$.exe $.pcm $sample_rate $channels $bits_per_sample $.wav\n"); printf("Example: $.exe in.pcm 44100 2 16 out.wav\n"); printf("$.exe $.pcm $.wav\n"); printf("Example: $.exe in.pcm out.wav\n"); return -1; } p2w->pcm_data = 0; p2w->in_file = NULL; p2w->out_file = NULL; memcpy(p2w->riff.ID, "RIFF", 4); memcpy(p2w->riff.format, "WAVE", 4); p2w->riff.size = 36; memcpy(p2w->fmt.ID, "fmt ", 4); p2w->fmt.size = 16; p2w->fmt.audio_format = 1; p2w->fmt.byte_rate = p2w->fmt.sample_rate * p2w->fmt.channels * p2w->fmt.bits_per_sample / 8; p2w->fmt.block_align = p2w->fmt.channels * p2w->fmt.bits_per_sample / 8; memcpy(p2w->data.ID, "data", 4); p2w->data.size = 0; return 0; }
- 转换过程
int PCM_to_WAV(PCM_to_WAV_t *p2w) { p2w->in_file = fopen(p2w->in_file_name, "rb+"); p2w->out_file = fopen(p2w->out_file_name, "wb+"); if (p2w->in_file == NULL || p2w->out_file == NULL) { printf("open file error\n"); return -1; } fseek(p2w->out_file, sizeof(struct RIFF_t), 1); fwrite(&p2w->fmt, sizeof(struct FMT_t), 1, p2w->out_file); fseek(p2w->out_file, sizeof(struct DATA_t), 1); fread(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->in_file); while (! feof(p2w->in_file) ) { p2w->data.size += 2; fwrite(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->out_file); fread(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->in_file); } p2w->riff.size += p2w->data.size; rewind(p2w->out_file); fwrite(&p2w->riff, sizeof(struct RIFF_t), 1, p2w->out_file); fseek(p2w->out_file, sizeof(struct FMT_t), 1); fwrite(&p2w->data, sizeof(struct DATA_t), 1, p2w->out_file); fclose(p2w->in_file); fclose(p2w->out_file); return 0; }
- 打印块信息
void PCM_to_WAV_Info(PCM_to_WAV_t *p2w) { printf("in file: %s\nout file: %s\n", p2w->in_file_name, p2w->out_file_name); printf("RIFF: \n\tID: %4.4s\n\tsize: %d\n\tformat: %4.4s\n", p2w->riff.ID, p2w->riff.size, p2w->riff.format); printf("FMT: \n\tID: %4.4s\n\tSize: %d\n\tAudio fmt: %d\n\tChannels: %d\n", p2w->fmt.ID, p2w->fmt.size, p2w->fmt.audio_format, p2w->fmt.channels); printf("\tsample rate: %d\n\tByte Rate: %d\n\tBlock Align: %d\n\tBits Per Sample: %d\n", p2w->fmt.sample_rate, p2w->fmt.byte_rate, p2w->fmt.block_align, p2w->fmt.bits_per_sample); printf("DATA: \n\tID: %4.4s\n\tSize: %d\n", p2w->data.ID, p2w->data.size); }
- 调用
int main(int argc, char **argv) { struct PCM_to_WAV_t pcm2wav; if (PCM_to_WAV_Init(&pcm2wav, argc, argv) != 0) return -1; if (PCM_to_WAV(&pcm2wav) != 0) return -1; PCM_to_WAV_Info(&pcm2wav); return 0; }
-
参考资料