录制PCM音频文件(单通道 8000Hz),并进行播放.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <alsa/asoundlib.h>
typedef struct _ec_snd_pcm_info {
snd_pcm_t *handle; //PCM设备句柄.
snd_pcm_stream_t stream; //PCM流类型 捕获流 回放流.
snd_pcm_access_t access_type; //访问类型.
snd_pcm_format_t format; //访问大小端格式.
unsigned int nChannels; //通道数.
unsigned int rate; //采样率.
snd_pcm_uframes_t psize_frames; //时段帧数.
snd_pcm_uframes_t bsize_frames; //缓冲帧数.
char *data_buf; //数据.
} ec_snd_pcm_info_t;
int record_param(ec_snd_pcm_info_t *info, snd_pcm_hw_params_t *¶ms)
{
int dir = 0;
//打开PCM设备.
int err = snd_pcm_open(&info->handle, "default", info->stream, 0);
if (err < 0) {
printf("unabble to open pcm device : %s\n", snd_strerror(err));
return -1;
}
//构造硬件参数空间.
err = snd_pcm_hw_params_malloc(¶ms);
if (err < 0) {
printf("malloc space for snd_pcm_hw_params_t structure failed %s!\n", snd_strerror(err));
return -1;
}
//初始化默认值.
err = snd_pcm_hw_params_any(info->handle, params);
if (err < 0) {
printf("Broken configuration for this PCM %s\n", snd_strerror(err));
return -1;
}
//设置访问类型.
err = snd_pcm_hw_params_set_access(info->handle, params, info->access_type);
if (err < 0) {
printf("Access type not available %s\n", snd_strerror(err));
return -1;
}
//设置大小端格式.
err = snd_pcm_hw_params_set_format(info->handle, params, info->format);
if (err < 0) {
printf("Sample format non available %s\n", snd_strerror(err));
return -1;
}
//设置通道.
err = snd_pcm_hw_params_set_channels(info->handle, params, info->nChannels);
if (err < 0) {
printf("Channels count non available %s\n", snd_strerror(err));
return -1;
}
//设置每秒采样率.
err = snd_pcm_hw_params_set_rate_near(info->handle, params, &info->rate, &dir);
if (err < 0) {
printf("Set rate failed %s\n", snd_strerror(err));
return -1;
}
//设置缓冲大小.
err = snd_pcm_hw_params_set_buffer_size_near(info->handle, params,&info->bsize_frames);
if (err < 0) {
printf("Set period size failed %s\n", snd_strerror(err));
return -1;
}
//设置时段大小.
err = snd_pcm_hw_params_set_period_size_near(info->handle, params, &info->psize_frames, &dir);
if (err < 0) {
printf("Set buffer size failed %s\n", snd_strerror(err));
return -1;
}
//参数设置到驱动.
err = snd_pcm_hw_params(info->handle, params);
if (err < 0) {
printf("failed to set hardware parameters:%s\n", snd_strerror(err));
return -1;
}
return 0;
}
int record_test(const char *filename)
{
if (NULL == filename || strlen(filename) <= 0) {
printf("filename is NULL!\n");
return -1;
}
remove(filename);
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 666);
if (fd < 0) {
printf("open file failed!\n");
return -1;
}
chmod(filename, 0777);
printf("open record pcm file ok!\n");
//设置参数.
snd_pcm_hw_params_t *params = NULL;
ec_snd_pcm_info_t info;
memset(&info, 0, sizeof(ec_snd_pcm_info_t));
info.stream = SND_PCM_STREAM_CAPTURE;
info.access_type = SND_PCM_ACCESS_RW_INTERLEAVED;
info.format = SND_PCM_FORMAT_S16_LE;
info.nChannels = 1;
info.rate = 8000;
info.psize_frames = 320;
info.bsize_frames = 640;
if (record_param(&info, params) < 0) {
close(fd);
remove(filename);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("open pcm device and set param ok!\n");
int dir = 0;
//获取时段大小.
int err = snd_pcm_hw_params_get_period_size(params, &info.psize_frames, &dir);
if (err < 0) {
printf("get period size fail %s\n", snd_strerror(err));
close(fd);
remove(filename);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get_period_size=%d!\n", info.psize_frames);
//获取时段时间.
unsigned int period_time = 0;
snd_pcm_hw_params_get_period_time(params, &period_time, &dir);
int loop = 1000000 / period_time;
loop *= 5;//录制5秒.
printf("get_period_time=%d, loop=%d!\n", period_time, loop);
info.data_buf = (char *)malloc(info.psize_frames * 2);
int count = 0;
while (loop--) {
err = snd_pcm_readi(info.handle, info.data_buf, info.psize_frames);
if (err == -EAGAIN || (err >= 0 && (size_t)err < info.psize_frames)) {
snd_pcm_wait(info.handle, 1000);
}
else if (err == -EPIPE) {
snd_pcm_prepare(info.handle);
}
else if (err == -ESTRPIPE) {
snd_pcm_resume(info.handle);
}
else if (err < 0) {
printf("Error snd_pcm_writei: [%s]", snd_strerror(err));
close(fd);
remove(filename);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
//将音频数据写入文件
count = write(fd, info.data_buf, info.psize_frames * 2);
if (count != info.psize_frames * 2) {
printf("short write: wrote %d bytes\n", count);
}
}
snd_pcm_drain(info.handle);
snd_pcm_close(info.handle);
printf("record pcm data ok!\n");
close(fd);
if (NULL != info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return 0;
}
int play_test(const char *filename)
{
if (NULL == filename || strlen(filename) <= 0) {
printf("filename is NULL!\n");
return -1;
}
int fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("open file failed!\n");
return -1;
}
printf("open play pcm file ok!\n");
//设置参数.
snd_pcm_hw_params_t *params = NULL;
ec_snd_pcm_info_t info;
memset(&info, 0, sizeof(ec_snd_pcm_info_t));
info.stream = SND_PCM_STREAM_PLAYBACK;
info.access_type = SND_PCM_ACCESS_RW_INTERLEAVED;
info.format = SND_PCM_FORMAT_S16_LE;
info.nChannels = 1;
info.rate = 8000;
info.psize_frames = 320;
info.bsize_frames = 640;
if (record_param(&info, params) < 0) {
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("open pcm device and set param ok!\n");
int dir = 0;
int err = snd_pcm_hw_params_get_period_size_min(params, &info.psize_frames, &dir);
if(err < 0) {
printf("get period size min falied!\n");
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get period size min = %d!\n", info.psize_frames);
err = snd_pcm_hw_params_get_period_size(params, &info.psize_frames, &dir);
if (err < 0) {
printf("get period size fail\n");
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get period size = %d!\n", info.psize_frames);
err = snd_pcm_hw_params_get_buffer_size_min(params, &info.bsize_frames);
if(err < 0) {
printf("get buffer size min falied!\n");
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get buffer size min = %d!\n", info.bsize_frames);
err = snd_pcm_hw_params_get_buffer_size(params, &info.bsize_frames);
if (err < 0) {
printf("get buffer size failed\n");
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get buffer size = %d!\n", info.bsize_frames);
unsigned int buffer_time = 0;
err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0);
if(err < 0) {
printf("Get buffer time failed!\n");
close(fd);
if (info.handle) {
snd_pcm_close(info.handle);
}
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return -1;
}
printf("get_buffer_time_max = %d!\n", buffer_time);
info.data_buf = (char*)malloc(info.psize_frames * 2);
int count = 0;
while (true) {
count = read(fd, info.data_buf, info.psize_frames * 2);
if (count <= 0) {
break;
}
while((err = snd_pcm_writei(info.handle, info.data_buf, info.psize_frames)) < 0) {
if(err == -EAGAIN) {
snd_pcm_wait(info.handle, 1000);
}
else if(err == -EPIPE) {
snd_pcm_prepare(info.handle);
}
else if(err == -ESTRPIPE) {
snd_pcm_resume(info.handle);
}
else if(err < 0) {
printf("snd_pcm_writei error:%s\n", snd_strerror(err));
}
}
}
snd_pcm_drain(info.handle);
snd_pcm_close(info.handle);
printf("play pcm data ok!\n");
close(fd);
if (info.data_buf) {
free(info.data_buf);
info.data_buf = NULL;
}
return 0;
}