实现
新建alsa_play_wav.c文件。
2.1 wav 解析
Wav解析与添加头代码如下
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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)
{
uint16_t cbsize = (uint16_t)p[0] | ((uint16_t)p[1]<<8);
p += 2;
if(cbsize > 0)
{
p += 2;
p += 4;
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;
}
static void wav_fill_head(uint8_t* buffer, int samples, int chnum, int freq)
{
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;
p[17] = 0;
p[18] = 0;
p[19] = 0;
p[20] = 1;
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;
p[29] = (bps>>8) & 0xFF;
p[30] = (bps>>16) & 0xFF;
p[31] = (bps>>24) & 0xFF;
p[32] = chnum*16/8;
p[33] = 0;
p[34] = 16;
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播放
打开设备与参数初始化
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) {
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) {
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);
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;
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_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;
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;
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;
}