来看下函数的执行流程:
1. 确认传参数目。没指定具体wav文件就打印帮助信息
if (argc < 2) {
fprintf(stderr, "Usage: %s file.wav [-D card] [-d device] [-p period_size]"
" [-n n_periods] \n", argv[0]);
return 1;
}
2. 打开wav文件进行操作
file = fopen(filename, "rb");
3. 读文件开头的信息,确认是wav或riff文件
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
if ((riff_wave_header.riff_id != ID_RIFF) ||
(riff_wave_header.wave_id != ID_WAVE)) {
fprintf(stderr, "Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
4. 进入循环,反复进行以下操作:读取一个块的头,判断接下来的信息是格式还是数据,如果是格式,那么就读取格式信息,并定位到格式信息末尾;如果是数据,则跳出循环;对于未知的块则定位到块头末尾
do {
fread(&chunk_header, sizeof(chunk_header), 1, file);
switch (chunk_header.id) {
case ID_FMT:
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
/* If the format header is larger, skip the rest */
if (chunk_header.sz > sizeof(chunk_fmt))
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
break;
case ID_DATA:
/* Stop looking for chunks */
more_chunks = 0;
chunk_header.sz = le32toh(chunk_header.sz);
break;
default:
/* Unknown chunk, skip bytes */
fseek(file, chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
5. 匹配后面的参数并填入
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
}
if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
}
if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
}
if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
}
if (*argv)
argv++;
}
6. 播放音频,最后关闭文件
play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,
chunk_fmt.bits_per_sample, period_size,
period_count, chunk_header.sz);
fclose(file);
先来看看函数原型
void play_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int channels, unsigned int rate,
unsigned int bits, unsigned int period_size,
unsigned int period_count, uint32_t data_sz)
然后来看看函数执行流程
1. 填入PCM流的相关信息
memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
if (bits == 32)
config.format = PCM_FORMAT_S32_LE;
else if (bits == 24)
config.format = PCM_FORMAT_S24_3LE;
else if (bits == 16)
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
2. 通过验证相关信息的合法性判断能否播放
if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count))
return;
3. 打开PCM设备
pcm = pcm_open(card, device, PCM_OUT, &config);
4. 开个buffer保存数据
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
5. 写入数据到PCM设备
do {
read_sz = size < data_sz ? size : data_sz;
num_read = fread(buffer, 1, read_sz, file);
if (num_read > 0) {
if (pcm_write(pcm, buffer, num_read)) {
fprintf(stderr, "Error playing sample\n");
break;
}
data_sz -= num_read;
}
} while (!close && num_read > 0 && data_sz > 0);
6. 释放buffer,关闭设备
free(buffer);
pcm_close(pcm);