tinyalsa-tinyplay源码浅析

main()函数

来看下函数的执行流程:

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);

play_sample()函数

先来看看函数原型

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);

你可能感兴趣的:(tinyalsa相关,c语言,音频编码解码,linux,alsa,android)