1.源码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#pragma pack(push, 1)
typedef struct {
char riff[4]; //"RIFF"
uint32_t file_size; //文件总大小
char wave[4]; //"WAVE"
char fmt[4]; //"fmt "
uint32_t fmt_size; //fmt chunk大小(至少16)
uint16_t audio_format; //1 = PCM
uint16_t num_channels; //声道数
uint32_t sample_rate; //采样率
uint32_t byte_rate; //每秒字节数
uint16_t block_align; //每样本字节数
uint16_t bits_per_sample; //位深度
char data[4]; //"data"
uint32_t data_size; //音频数据大小
} WavHeader;
#pragma pack(pop)
int play_wav(const char *filename, const char *alsa_device)
{
FILE *file = fopen(filename, "rb");
if (!file)
{
perror("can not open file\n");
return -1;
}
//1.读取并验证wav头
WavHeader header;
if (fread(&header, sizeof(WavHeader), 1, file) != 1)
{
fprintf(stderr, "file header read failed\n");
fclose(file);
return -2;
}
if (memcmp(header.riff, "RIFF", 4) != 0 ||
memcmp(header.wave, "WAVE", 4) != 0 ||
memcmp(header.fmt, "fmt ", 4) != 0 ||
header.audio_format != 1)
{
fprintf(stderr, "not pcm wav file\n");
fclose(file);
return -3;
}
//2.配置ALSA设备
snd_pcm_t *pcm;
int err;
if ((err == snd_pcm_open(&pcm, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
fprintf(stderr, "alsa device open failed: %s\n", snd_strerror(err));
fclose(file);
return -4;
}
//根据wav参数设置硬件
snd_pcm_format_t format;
if (header.bits_per_sample == 16)
{
format = SND_PCM_FORMAT_S16_LE;
}
else if (header.bits_per_sample == 24)
{
format = SND_PCM_FORMAT_S24_LE;
}
else if (header.bits_per_sample == 32)
{
format = SND_PCM_FORMAT_S32_LE;
}
else
{
fprintf(stderr, "not support bit deep: %d\n", header.bits_per_sample);
fclose(file);
snd_pcm_close(pcm);
return -5;
}
if ((err = snd_pcm_set_params(
pcm,
format,
SND_PCM_ACCESS_RW_INTERLEAVED,
header.num_channels,
header.sample_rate,
1,
50000)
) < 0
)
{
fprintf(stderr, "params set failed: %s\n", snd_strerror(err));
fclose(file);
snd_pcm_close(pcm);
return -6;
}
//3.播放音频数据
const size_t buffer_size = 4096;
uint8_t *buffer = malloc(buffer_size);
size_t remaining = header.data_size;
while (remaining > 0)
{
size_t to_read = (remaining > buffer_size) ? buffer_size : remaining;
size_t read = fread(buffer, 1, to_read, file);
if (read == 0)
{
break;
}
snd_pcm_uframes_t frames = read / header.block_align;
int write_result = snd_pcm_writei(pcm, buffer, frames);
if (write_result == -EPIPE)
{
fprintf(stderr, "no prepare buffer\n");
snd_pcm_prepare(pcm);
}
else if (write_result < 0)
{
fprintf(stderr, "Write failed: %s\n", snd_strerror(write_result));
break;
}
remaining -= read;
}
//4.等待播放完成
snd_pcm_drain(pcm);
//5.清理资源
free(buffer);
fclose(file);
snd_pcm_close(pcm);
return 0;
}
int main(int argc, char **argv)
{
if (argc != 3)
{
return -1;
}
return play_wav(argv[1], argv[2]);
}
2.编译源码
$ gcc -g -o alsa_demo alsa_demo.c -lasound -lm