一. 什么是wav
保存的音频文件是wav(waveform audio file format),wav是一种用于存储音频数据的文件格式,由微软和ibm联合开发。wav文件是无损的,存储的是原始音频数据,没有经过任意压缩或丢失信息。WAV 文件通常用于专业音频编辑、录音和音频播放。
wav文件格式
wav文件由多个块(chunk)组成,每个块都有特定的功能。以下是wav文件的基本结构:
- riff标头:包含文件的标识符和文件大小,大小为12字节
char riff[4]; // "RIFF"
int filesize; // 文件大小
char wave[4]; // "WAVE"
- fmt块(格式块):包含音频格式的信息,如采样率、声道数等,大小为16-18字节
char fmt_chunk_marker[4]; // "fmt "
int fmt_chunk_len; // 格式块长度 (通常是16)
short audio_format; // 音频格式 (1 表示 PCM)
short num_channels; // 声道数 (1 单声道, 2 立体声)
int sample_rate; // 采样率 (例如 44100 Hz)
int byte_rate; // 每秒字节数 (sample_rate * num_channels * (bits_per_sample/8))
short frame_size; // 每个样本的字节数 (num_channels * (bits_per_sample/8))
short bits_per_sample; // 每个样本的位数 (例如 16)
fmt_chunk_len 是用于标识 fmt 块长度的字段,通常是 16,它告诉解析 WAV 文件的软件或库,fmt 块中包含了多少字节的数据。这有助于正确解析音频格式信息。
num_channels 字段表示音频数据的声道数。
3. data块(数据块):包含实际的音频数据,大小取决于音频数据的长度
char data_chunk_header[8]; // "data"int data_bytes; // 数据字节数
二. 音频专业词的解释
常见的声道数包括:单声道 (Mono): 一个声道、立体声 (Stereo): 两个声道
单声道 (Mono)
- 定义:只有一个声道,意味着所有声音都从同一个通道输出,听起来像是来自同一个方向的声音。
- 用途:适合录制单一音源(如人声、乐器独奏等)。
- 文件大小:因为只有一个声道,所以文件大小比立体声小一半。
立体声 (Stereo)
- 定义:有两个声道,通常是左声道和右声道,可以提供空间感和方位感。
- 用途:适合音乐录制和播放,因为可以提供更好的听觉体验,感觉声音是来自不同的方向。
- 文件大小:因为有两个声道,所以文件大小大约是单声道的两倍。
在大多数情况下,单声道 是语音识别的首选。原因如下:
- 数据简化:单声道音频只需要处理一个声道的数据,简化了语音识别算法的实现和优化。
- 计算效率:处理单声道音频通常更快,更适合实时语音识别应用。
- 存储和传输:单声道文件更小,减少了存储和传输的成本。
采样频率(sample rate)是一个非常重要的参数,它决定了每秒钟从模拟信息中提取多少个样本。常见的采样频率包括有8,000 Hz (8 kHz)、16,000 Hz (16 kHz)、22,050 Hz (22.05 kHz)和44,100 Hz (44.1 kHz)等
8,000 Hz (8 kHz)特点:低采样率,数据量小。质量:音质较低,通常只适用于语音通信,如电话和无线电通话。应用场景:电话网络、语音识别系统、低带宽通信
16,000 Hz (16 kHz)特点:中等采样率,数据量适中。质量:比 8 kHz 好,但仍主要用于语音通信。
应用场景:语音识别、语音助手、一些视频会议系统
22,050 Hz (22.05 kHz)特点:中等偏高的采样率,数据量较大。质量:音质较好,适合某些音乐和高质量语音应用。应用场景:CD 音频的一半速率(CD 音频标准是 44.1 kHz),有时用于压缩音频文件、某些专业录音设备、一些广播和流媒体应用
44,100 Hz (44.1 kHz)特点:高采样率,数据量大。质量:高质量音频,几乎可以完美再现大部分人类听觉范围内的声音。应用场景:CD 音轨和数字音频文件、音乐制作和专业录音、多媒体应用、高质量音频流媒体
还可以一个一个试,就这几个常见的采样频率。
修改采样频率的代码
从原始音频数据生成一个具有指定采样率的新音频数据。它通过线性插值的方法实现重采样,并更新音频缓冲区的相应属性(数据、帧数和采样率)。
int resample_audio(audio_buffer_t *audio, int desired_sample_rate) {int original_sample_rate = audio->sample_rate;int original_length = audio->num_frames;int out_length = (int)round((double)original_length * (double)desired_sample_rate / (double)original_sample_rate);printf("Resampling: %d Hz -> %d Hz\n", original_sample_rate, desired_sample_rate);// 分配新的数据缓冲区float *resampled_data = (float *)malloc(out_length * sizeof(float));if (!resampled_data) {fprintf(stderr, "Memory allocation failed\n");return -1;}for (int i = 0; i < out_length; ++i) {double src_index = i * (double)original_sample_rate / (double)desired_sample_rate;int left_index = (int)floor(src_index);int right_index = (left_index + 1 < original_length) ? left_index + 1 : left_index;double fraction = src_index - left_index;// 边界检查if (left_index < 0) left_index = 0;if (right_index >= original_length) right_index = original_length - 1;resampled_data[i] = (1.0f - fraction) * audio->data[left_index] + fraction * audio->data[right_index];}// 更新 audio 缓冲区free(audio->data);audio->data = resampled_data;audio->num_frames = out_length;audio->sample_rate = desired_sample_rate;return 0;
}