实现
新建alsa_rec_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_8c-example.html
打开设备与参数初始化
int alsa_init(char* device, snd_pcm_t **handle, int ch, int freq)
{
int err;
if ((err = snd_pcm_open(handle, device, SND_PCM_STREAM_CAPTURE, 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,
ch,
freq,
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,ch);
snd_pcm_hw_params_set_rate(*handle, hwparams,freq,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);
snd_pcm_start(*handle);
return 0;
}
采集
int alsa_rec(snd_pcm_t *handle, void* buffer, size_t size)
{
int timeout = 0;
snd_pcm_sframes_t frames;
do{
frames = snd_pcm_avail_update(handle);
if(frames < size){
usleep(1000);
timeout++;
if(timeout >= 1000){
printf("rec timeout\n");
return -1;
}
}
}while(frames < size);
frames = snd_pcm_readi(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 CH_MAX 2
int main(int argc, char* argv[])
{
snd_pcm_t *handle = NULL;
int res;
FILE *wav_fd;
int16_t wav_buf[NN*CH_MAX];
uint8_t wav_head_buf[44]; /* wav文件头缓存 */
wav_t wav;
int ch;
int samps; /* 采样点数 */
int times; /* 读取次数 */
int sampleRate;
if(argc != 6){
printf("usage:alsa_rec_wav dev wav ch freq samps\r\n");
return -1;
}
ch = atoi(argv[3]);
sampleRate = atoi(argv[4]);
samps = atoi(argv[5]);
printf("ch=%d,freq=%d,samps=%d\r\n",ch,sampleRate,samps);
wav_fd = fopen(argv[2], "wb+");
if(wav_fd < 0){
printf("open file %s err\r\n",argv[2]);
return -2;
}
times = samps / NN; /* 一次读取NN个点,读取times次 */
printf("fill head\r\n");
wav_fill_head(wav_head_buf, times*NN, ch, sampleRate); /* 输出文件头 */
if(44 != fwrite(wav_head_buf, 1, 44, wav_fd)){
printf("write file %s err\n",argv[2]);
fclose(wav_fd);
return -3;
}
printf("alsa_init\r\n");
res = alsa_init(argv[1], &handle, ch, sampleRate);
if(res != 0){
fclose(wav_fd);
printf("alsa_init err\r\n");
return -4;
}
printf("alsa_rec\r\n");
for(int i=0; i<times; i++)
{
if(alsa_rec(handle, wav_buf, NN) < 0){ /* 注意这里最后一个参数是frames为单位 不需要乘以ch */
printf("rec %s err\r\n");
fclose(wav_fd);
alsa_deinit(handle);
return -5;
}
if(NN*ch != fwrite(wav_buf, sizeof(int16_t), NN*ch, wav_fd)){
printf("write file %s err\r\n",argv[2]);
fclose(wav_fd);
alsa_deinit(handle);
return -6;
}
}
fclose(wav_fd);
alsa_deinit(handle);
return 0;
}