main()函数
先来看看函数的执行流程
1. 定义、初始化相关参数
FILE *file;
struct wav_header header;
unsigned int card = 0;
unsigned int device = 0;
unsigned int channels = 2;
unsigned int rate = 44100;
unsigned int bits = 16;
unsigned int frames;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int cap_time = 0;
enum pcm_format format;
2. 不输入参数则打印出可以选择的参数
if (argc < 2) {
fprintf(stderr, "Usage: %s file.wav [-D card] [-d device]"
" [-c channels] [-r rate] [-b bits] [-p period_size]"
" [-n n_periods] [-T capture time]\n", argv[0]);
return 1;
}
3. 打开或创建用于保存数据的文件
file = fopen(argv[1], "wb");
4. 分析传进来的参数,并填充
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
} else if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
} else if (strcmp(*argv, "-T") == 0) {
argv++;
if (*argv)
cap_time = atoi(*argv);
}
if (*argv)
argv++;
}
header.riff_id = ID_RIFF;
header.riff_sz = 0;
header.riff_fmt = ID_WAVE;
header.fmt_id = ID_FMT;
header.fmt_sz = 16;
header.audio_format = FORMAT_PCM;
header.num_channels = channels;
header.sample_rate = rate;
switch (bits) {
case 32:
format = PCM_FORMAT_S32_LE;
break;
case 24:
format = PCM_FORMAT_S24_LE;
break;
case 16:
format = PCM_FORMAT_S16_LE;
break;
default:
fprintf(stderr, "%u bits is not supported.\n", bits);
fclose(file);
return 1;
}
header.bits_per_sample = pcm_format_to_bits(format);
header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
header.block_align = channels * (header.bits_per_sample / 8);
header.data_id = ID_DATA;
5. 为头部信息留出空间
fseek(file, sizeof(struct wav_header), SEEK_SET);
6. 设置好信号处理函数,并开始数据采集
signal(SIGINT, sigint_handler);
signal(SIGHUP, sigint_handler);
signal(SIGTERM, sigint_handler);
frames = capture_sample(file, card, device, header.num_channels,
header.sample_rate,format, period_size,
period_count, cap_time);
7. 数据采集完之后,头部所需信息也已知,填充进去即可
header.data_sz = frames * header.block_align;
header.riff_sz = header.data_sz + sizeof(header) - 8;
fseek(file, 0, SEEK_SET);
fwrite(&header, sizeof(struct wav_header), 1, file);
8. 最后关闭文件
fclose(file);
capture_sample()函数
这个函数用于录音,先来看看函数原型
unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int channels, unsigned int rate,
enum pcm_format format, unsigned int period_size,
unsigned int period_count, unsigned int cap_time)
再来按顺序看看涉及到的函数
1. 打开一个流
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, struct pcm_config *config);
2. 根据传入的格式类型返回对应的一帧的空间大小
unsigned int pcm_format_to_bits(enum pcm_format format);
3. 开一个buffer作为缓冲区
buffer = malloc(size);
4. 设置时间。这里now和end代表了开始和结束,相当于定时
clock_gettime(CLOCK_MONOTONIC, &now);
end.tv_sec = now.tv_sec + cap_time;
end.tv_nsec = now.tv_nsec;
5. 进入循环,写入数据,到时间了也会停止
while (capturing && !pcm_read(pcm, buffer, size)) {
if (fwrite(buffer, 1, size, file) != size) {
fprintf(stderr,"Error capturing sample\n");
break;
}
bytes_read += size;
if (cap_time) {
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > end.tv_sec ||
(now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
break;
}
}
6. 做收尾工作并返回帧数(用于计算总大小,填充文件头部信息)
frames = pcm_bytes_to_frames(pcm, bytes_read);
free(buffer);
pcm_close(pcm);
return frames;