实现
新建alsa_play_wav.c文件。
2.1 wav 解析
Wav格式解析参考公众号文章
https://mp.weixin.qq.com/s/Lmf1rAi-U4B-ZbZbMsuwqA
Wav解析与添加头代码如下
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* WAV解析 */
#define CHUNK_RIFF "RIFF"
#define CHUNK_WAVE "WAVE"
#define CHUNK_FMT "fmt "
#define CHUNK_DATA "data"
typedef struct
{
uint32_t off;
uint32_t chunksize;
uint16_t audioformat;
uint16_t numchannels;
uint32_t samplerate;
uint32_t byterate;
uint16_t blockalign;
uint16_t bitspersample;
uint32_t datasize;
}wav_t;
static int wav_decode_head(uint8_t* buffer, wav_t* wav)
{
uint8_t* p = buffer;
uint32_t chunksize;
uint32_t subchunksize;
if(0 != memcmp(p,CHUNK_RIFF,4))
{
return -1;
}
p += 4;
chunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
wav->chunksize = chunksize;
p += 4;
if(0 != memcmp(p,CHUNK_WAVE,4))
{
return -2;
}
p += 4;
do
{
if(0 == memcmp(p,CHUNK_FMT,4))
{
p += 4;
subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
p += 4;
/* 解析参数 */
wav->audioformat = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
if((wav->audioformat == 0x0001) || (wav->audioformat == 0xFFFE))
{
p += 2;
wav->numchannels = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
p += 2;
wav->samplerate = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
p += 4;
wav->byterate = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
p += 4;
wav->blockalign = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
p += 2;
wav ->bitspersample = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
p += 2;
if(subchunksize >16)
{
/* 有ext区域 */
uint16_t cbsize = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
p += 2;
if(cbsize > 0)
{
/* ext数据 2字节有效bits wValidBitsPerSample ,4字节dwChannelMask 16字节SubFormat */
p += 2;
p += 4;
/* 比对subformat */
p += 16;
}
}
}
else
{
p += subchunksize;
}
}
else if(0 == memcmp(p,CHUNK_DATA,4))
{
p += 4;
subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
wav->datasize = subchunksize;
p += 4;
wav->off = (uint32_t)(p- buffer);
return 0;
}
else
{
p += 4;
subchunksize = (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24);
p += 4;
p += subchunksize;
}
}while((uint32_t)(p - buffer) < (chunksize + 8));
return -3;
}
/* 填充44字节的wav头 */
static void wav_fill_head(uint8_t* buffer, int samples, int chnum, int freq)
{
/*
* 添加wav头信息
*/
uint32_t chunksize = 44-8+samples*chnum*16/8;
uint8_t* p = (uint8_t*)buffer;
uint32_t bps = freq*chnum*16/8;
uint32_t datalen = samples*chnum*16/8;
p[0] = 'R';
p[1] = 'I';
p[2] = 'F';
p[3] = 'F';
p[4] = chunksize & 0xFF;
p[5] = (chunksize>>8) & 0xFF;
p[6] = (chunksize>>16) & 0xFF;
p[7] = (chunksize>>24) & 0xFF;
p[8] = 'W';
p[9] = 'A';
p[10] = 'V';
p[11] = 'E';
p[12] = 'f';
p[13] = 'm';
p[14] = 't';
p[15] = ' ';
p[16] = 16; /* Subchunk1Size */
p[17] = 0;
p[18] = 0;
p[19] = 0;
p[20] = 1; /* PCM */
p[21] = 0;
p[22] = chnum; /* 通道数 */
p[23] = 0;
p[24] = freq & 0xFF;
p[25] = (freq>>8) & 0xFF;
p[26] = (freq>>16) & 0xFF;
p[27] = (freq>>24) & 0xFF;
p[28] = bps & 0xFF; /* ByteRate */
p[29] = (bps>>8) & 0xFF;
p[30] = (bps>>16) & 0xFF;
p[31] = (bps>>24) & 0xFF;
p[32] = chnum*16/8; /* BlockAlign */
p[33] = 0;
p[34] = 16; /* BitsPerSample */
p[35] = 0;
p[36] = 'd';
p[37] = 'a';
p[38] = 't';
p[39] = 'a';
p[40] = datalen & 0xFF;
p[41] = (datalen>>8) & 0xFF;
p[42] = (datalen>>16) & 0xFF;
p[43] = (datalen>>24) & 0xFF;
}
void wav_print(wav_t* wav)
{
printf("off:%d\r\n",wav->off);
printf("chunksize:%d\r\n",wav->chunksize);
printf("audioformat:%d\r\n",wav->audioformat);
printf("numchannels:%d\r\n",wav->numchannels);
printf("samplerate:%d\r\n",wav->samplerate);
printf("byterate:%d\r\n",wav->byterate);
printf("blockalign:%d\r\n",wav->blockalign);
printf("bitspersample:%d\r\n",wav->bitspersample);
printf("datasize:%d\r\n",wav->datasize);
}
2.2 alsa播放
参考
https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html
打开设备与参数初始化
int alsa_init(char* device, snd_pcm_t **handle, wav_t* wav)
{
int err;
if ((err = snd_pcm_open(handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { /* 最后一个参数0 阻塞模式 1非阻塞模式 */
printf("Playback open error: %s\n", snd_strerror(err));
return -1;
}
snd_pcm_sframes_t frames;
if ((err = snd_pcm_set_params(*handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
wav->numchannels,
wav->samplerate,
0,
100000)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
snd_pcm_close(*handle);
return -2;
}
snd_pcm_hw_params_t *hwparams = NULL;
snd_pcm_hw_params_malloc(&hwparams);
snd_pcm_hw_params_any(*handle, hwparams);
snd_pcm_hw_params_set_access(*handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(*handle, hwparams,SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(*handle, hwparams,wav->numchannels);
snd_pcm_hw_params_set_rate(*handle, hwparams,wav->samplerate,0);
snd_pcm_hw_params_set_period_size(*handle, hwparams, NN, 0);
snd_pcm_hw_params_set_buffer_size(*handle, hwparams, 4*NN);
snd_pcm_hw_params(*handle, hwparams);
snd_pcm_hw_params_free(hwparams);
return 0;
}
播放
int alsa_play(snd_pcm_t *handle, const void* buffer, size_t size)
{
snd_pcm_sframes_t frames;
frames = snd_pcm_writei(handle, buffer, (snd_pcm_uframes_t)size); /* 注意这里的size为frames即点数 而不是字节大小 */
if (frames < 0){
frames = snd_pcm_recover(handle, frames, 0);
printf("snd_pcm_recover: %s\n", snd_strerror(frames));
}
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
return -1;
}
if (frames > 0 && frames < (long)sizeof(buffer)){
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
return -2;
}
return 0;
}
完成关闭
int alsa_deinit(snd_pcm_t *handle)
{
int err;
/* pass the remaining samples, otherwise they're dropped in close */
err = snd_pcm_drain(handle);
if (err < 0){
printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
}
snd_pcm_close(handle);
return 0;
}
2.3 读取wav文件进行播放
#include <alsa/asoundlib.h>
#define NN 128
#define MAX_CH 2
int main(int argc, char* argv[])
{
snd_pcm_t *handle = NULL;
int res;
FILE *wav_fd;
int16_t wav_buf[NN*MAX_CH];
uint8_t wav_head_buf[128]; /* 输入wav文件头缓存 */
wav_t wav;
int samps; /* 采样点数 */
int times; /* 读取次数 */
int sampleRate;
if(argc != 3){
printf("usage:alsa_play_wav dev wav\r\n");
return -1;
}
wav_fd = fopen(argv[2], "rb");
if(wav_fd < 0){
printf("open file %s err\r\n",argv[2]);
return -2;
}
if(sizeof(wav_head_buf) != fread(wav_head_buf, 1, sizeof(wav_head_buf), wav_fd)){
printf("read file %s err\r\n",argv[2]);
fclose(wav_fd);
return -3;
}
if(0 != (res=wav_decode_head(wav_head_buf, &wav))){
printf("decode file %s err %d\r\n",argv[2],res);
fclose(wav_fd);
return -4;
}
printf("[wav]\r\n");
wav_print(&wav);
samps = wav.datasize;
samps /= wav.blockalign; /* 采样点数 = 数据大小 除以 blockalign */
printf("\r\nsamps:%d\r\n",samps);
sampleRate = wav.samplerate;
res = alsa_init(argv[1], &handle, &wav);
if(res != 0){
fclose(wav_fd);
printf("alsa_init err\r\n");
return -5;
}
times = samps / NN; /* 一次读取NN个点,读取times次 */
fseek(wav_fd,wav.off,SEEK_SET);
for(int i=0; i<times; i++)
{
if(NN*wav.numchannels != fread(wav_buf, sizeof(int16_t), NN*wav.numchannels, wav_fd)){
printf("read file %s err\r\n",argv[2]);
fclose(wav_fd);
alsa_deinit(handle);
return -6;
}
if(alsa_play(handle, wav_buf, NN) < 0){
printf("play %s err\r\n");
fclose(wav_fd);
alsa_deinit(handle);
return -7;
}
}
fclose(wav_fd);
alsa_deinit(handle);
return 0;
}