alsa-录制和播放的详细过程
- 前言
- 一。播放基本原理和过程(伪代码)
- 1. 打开pcm音频文件
- 2. 打开音频设备
- 3. 设置播放的参数
- 4.将设置好的参数写入设备
- 5. 计算每个周期的音频数据大小
- 6. 把音频文件的数据全部读出到缓存中
- 7. 循环写入数据
- 8. 关闭音频设备并初始化
- 二。录音的基本原理和过程(伪代码)
- 1. 打开pcm音频文件
- 2. 打开音频设备
- 3. 设置录制的参数
- 4.将设置好的参数写入设备
- 5. 计算每个周期的音频数据大小
- 6. 为读取的每一帧音频分配缓存
- 7. 循环写入数据
- 8. 关闭音频设备并初始化
- 参考资料
前言
网上很多资料都提到怎么录制和播放wav音频文件,但是很少提及原始pcm音频文件的录制和播放,下面我将结合网上资料和自己的理解梳理用alsa录制和播放原始pcm音频文件的原理和过程。
一。播放基本原理和过程(伪代码)
1. 打开pcm音频文件
fp = fopen(DEFAULT_PATH, "rb");//DEFAULT_PATH为pcm音频文件的地址
2. 打开音频设备
snd_pcm_t* handle = NULL;
//以音频模式打开,handle为音频设备的句柄
snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
3. 设置播放的参数
snd_pcm_hw_params_t* params = NULL;
snd_pcm_hw_params_alloca(¶ms); //分配params结构体
snd_pcm_hw_params_any(handle, params);//初始化params
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //设置访问类型
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);//设置数据格式
snd_pcm_hw_params_set_channels(handle, params, channels); //设置声道,1表示单声道,2表示立体声
unsigned int val = 44100;
int dir=0;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); //设置频率
- 补充说明:
一。
snd_pcm_hw_params_set_access
:设置访问类型:
SND_PCM_ACCESS_RW_INTERLEAVED
:
交错访问。在缓冲区的每个 PCM 帧都包含所有设置的声道的连续的采样数据。比如声卡要播放采样长度是 16-bit 的 PCM立体声数据,表示每个 PCM 帧中有 16-bit 的左声道数据,然后是 16-bit 右声道数据。
SND_PCM_ACCESS_RW_NONINTERLEAVED
:
非交错访问。每个 PCM 帧只是一个声道需要的数据,如果使用多个声道,那么第一帧是第一个声道的数据,第二帧是第二个声道的数据,依此类推。二。
snd_pcm_hw_params_set_format
:设置数据格式:
主要控制输入的音频数据的类型、无符号还是有符号、是little-endian 还是 bit-endian:比如对于 16-bit 长度的采样数据可以设置为:
SND_PCM_FORMAT_S16_LE 有符号16 bit Little Endian
SND_PCM_FORMAT_S16_BE 有符号16 bit Big Endian
SND_PCM_FORMAT_U16_LE 无符号16 bit Little Endian
SND_PCM_FORMAT_U16_BE 无符号 16 bit Big Endian
比如对于 32-bit 长度的采样数据可以设置为:
SND_PCM_FORMAT_S32_LE 有符号32 bit Little Endian
SND_PCM_FORMAT_S32_BE 有符号32 bit Big Endian
SND_PCM_FORMAT_U32_LE 无符号32 bit Little Endian
SND_PCM_FORMAT_U32_BE 无符号 32 bit Big Endian
4.将设置好的参数写入设备
snd_pcm_hw_params(handle, params);
5. 计算每个周期的音频数据大小
//获取一个周期有多少帧数据,一个周期一个周期方式处理音频数据。
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
int size;
//每帧数据4个字节,每个周期的数据大小为size
size = frames * 4;
6. 把音频文件的数据全部读出到缓存中
char *buf;
int file_size;//用ls -al等其他方式去获取到音频文件的大小
buf = (char*)malloc(file_size+ 4096);//分配缓存,这里+4096是确保可以完全读出音频文件
memset(buf, 0x00, file_size);//将缓存区清零
fread(buf , 1, file_size, fp);//将音频文件的数据全部读取到缓存区
7. 循环写入数据
int count_value = 0;//count_value 是用来协助“按顺序将缓存写入文件”
while(1)
{
snd_pcm_writei(handle, buf + count_value, frames);//按顺序将缓存写入文件
count_value = count_value + size;//更新下一帧存储的位置
if(count_value > file_size) //缓存区的数据全部写入完成,则退出循环。
{
break;
}
usleep(5); //每一帧数据写入完成,需要等待5ms再写入下一帧。
}
8. 关闭音频设备并初始化
close(fp);//关闭打开的源文件
snd_pcm_drop(handle);//关闭音频设备
snd_pcm_close(handle);
free(buf);//清空缓存
二。录音的基本原理和过程(伪代码)
1. 打开pcm音频文件
FILE * out_fd;
out_fd = fopen("./out_pcm.raw","wb+");//"./out_pcm.raw"为将要录制的音频文件的存储地址
其中
.raw
代表着音频文件为原始的pcm音频文件。
2. 打开音频设备
snd_pcm_t* handle = NULL;
//以录音模式打开,handle为音频设备的句柄
snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
3. 设置录制的参数
snd_pcm_hw_params_t* params = NULL;
snd_pcm_hw_params_alloca(¶ms); //分配params结构体
snd_pcm_hw_params_any(handle, params);//初始化params
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //设置访问类型
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);//设置数据格式
snd_pcm_hw_params_set_channels(handle, params, channels); //设置声道,1表示单声道,2表示立体声
unsigned int val = 44100;
int dir=0;
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); //设置频率
frames = 256;
snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir);//设置周期的数量(帧数)
- 补充说明:
一。
snd_pcm_hw_params_set_access
:设置访问类型:
SND_PCM_ACCESS_RW_INTERLEAVED
:
交错访问。在缓冲区的每个 PCM 帧都包含所有设置的声道的连续的采样数据。比如声卡要播放采样长度是 16-bit 的 PCM立体声数据,表示每个 PCM 帧中有 16-bit 的左声道数据,然后是 16-bit 右声道数据。
SND_PCM_ACCESS_RW_NONINTERLEAVED
:
非交错访问。每个 PCM 帧只是一个声道需要的数据,如果使用多个声道,那么第一帧是第一个声道的数据,第二帧是第二个声道的数据,依此类推。二。
snd_pcm_hw_params_set_format
:设置数据格式:
主要控制输入的音频数据的类型、无符号还是有符号、是little-endian 还是 bit-endian:比如对于 16-bit 长度的采样数据可以设置为:
SND_PCM_FORMAT_S16_LE 有符号16 bit Little Endian
SND_PCM_FORMAT_S16_BE 有符号16 bit Big Endian
SND_PCM_FORMAT_U16_LE 无符号16 bit Little Endian
SND_PCM_FORMAT_U16_BE 无符号 16 bit Big Endian
比如对于 32-bit 长度的采样数据可以设置为:
SND_PCM_FORMAT_S32_LE 有符号32 bit Little Endian
SND_PCM_FORMAT_S32_BE 有符号32 bit Big Endian
SND_PCM_FORMAT_U32_LE 无符号32 bit Little Endian
SND_PCM_FORMAT_U32_BE 无符号 32 bit Big Endian
4.将设置好的参数写入设备
snd_pcm_hw_params(handle, params);
5. 计算每个周期的音频数据大小
//获取一个周期有多少帧数据,一个周期一个周期方式处理音频数据。
snd_pcm_hw_params_get_period_size(params, &frames, &dir);
int size;
//每帧数据4个字节,每个周期的数据大小为size
size = frames * 4;
6. 为读取的每一帧音频分配缓存
char *buf;
int file_size;
buf = (char*)malloc(file_size);//分配缓存
7. 循环写入数据
snd_pcm_hw_params_get_period_time(params, &val, &dir);//将每一帧(每个周期)的时间获取到val
long loops = 5000000 / val; // 记录5秒长的声音
while( loops > 0 )
{
loops--;
snd_pcm_readi(handle,buffer,frames); //读取音频设备的数据于缓存
// 将音频数据写入文件
fwrite(buffer, 1, size, out_fd);
}
8. 关闭音频设备并初始化
snd_pcm_drop(handle);//关闭音频设备
snd_pcm_close(handle);
free(buffer);//清空缓存
fclose(out_fd);//关闭打开的源文件
参考资料
- alsa学习–4.用alsa播放wav文件
- PCM音频设备的操作(转)
- c语言实现ALSA录音