编译:
gcc read_microphone.c -o read_microphone -lasound
生成read_microphone可执行文件,运行:
./read_microphone
注意:此处采用6麦麦克风阵列
音频设备名,可以使用audacity软件查看,hw:1,0
或使用aplay -l 查看
//https://blog.csdn.net/maowendi/article/details/82348690
// gcc read_microphone.c -o read_microphone -lasound
/*
read from the default PCM device and writes to standard output for 5 seconds of data
修改声音采集配置时候,除了修改声音通道数量,还应该考虑申请的缓冲区时候足够大
*/
#define ALSA_PCM_NEW_HW_PARAMS_API
#include </usr/include/alsa/asoundlib.h>
int main()
{
long loops; //一个长整型变量,
int rc; //一个int变量 ,用来存放 snd_pcm_open(访问硬件)的返回值
int size; //一个int变量
snd_pcm_t * handle; // 一个指向snd_pcm_t的指针
snd_pcm_hw_params_t * params; // 一个指向 snd_pcm_hw_params_t的指针
unsigned int val; // 无符号整型变量 ,用来存放录音时候的采样率
int dir; // 整型变量
snd_pcm_uframes_t frames; // snd_pcm_uframes_t 型变量
char * buffer; // 一个字符型指针
FILE * out_fd; // 一个指向文件的指针
out_fd = fopen("out_pcm.raw","wb+"); /* 将流与文件之间的关系建立起来,文
件名为 out_pcm.raw,w是以文本方式
打开文件,wb是二进制方式打开文件wb+
读写打开或建立一个二进制文件,允许读和写。*/
/* open PCM device for recording (capture). */
// 访问硬件,并判断硬件是否访问成功
// hw:直接访问硬件 plughw:经过采样率和格式转换插件
//rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_CAPTURE, 0);
//rc = snd_pcm_open(&handle, "hw:1,0",SND_PCM_STREAM_CAPTURE,0);
rc = snd_pcm_open(&handle, "plughw:1,0",SND_PCM_STREAM_CAPTURE,0);
//printf("%d",rc);
if( rc < 0 )
{
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}
/* allocate a hardware parameters object */
// 分配一个硬件变量对象
snd_pcm_hw_params_alloca(¶ms);
/* fill it with default values. */
// 按照默认设置对硬件对象进行设置
snd_pcm_hw_params_any(handle,params);
/* set the desired hardware parameters */
/* interleaved mode 设置数据为交叉模式*/
snd_pcm_hw_params_set_access(handle,params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* signed 16-bit little-endian format */
// 设置数据编码格式为PCM、有符号、16bit、LE格式
snd_pcm_hw_params_set_format(handle,params,
SND_PCM_FORMAT_S16_LE);
/* two channels(stereo) */
// 设置单声道/多声道
snd_pcm_hw_params_set_channels(handle,params,8);
/* sampling rate */
// 设置采样率
val = 44100;
snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir);
/* set period size */
// 周期长度(帧数)
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir);
/* write parameters to the driver */
// 将配置写入驱动程序中
// 判断是否已经配置正确
rc = snd_pcm_hw_params(handle,params);
if ( rc < 0 )
{
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
exit(1);
}
/* use a buffer large enough to hold one period */
// 配置一个缓冲区用来缓冲数据,缓冲区要足够大,此处看意思应该是只配置了
// 够两个声道用的缓冲内存
snd_pcm_hw_params_get_period_size(params,&frames,&dir);
size = frames * 16; // 2 bytes/sample, 2channels
buffer = ( char * ) malloc(size);
/* loop for 5 seconds */
// 记录五秒长的声音
snd_pcm_hw_params_get_period_time(params, &val, &dir);
loops = 5000000 / val;
while( loops > 0 )
{
loops--;
rc = snd_pcm_readi(handle,buffer,frames); //从声卡读取声音数据
printf("%d\n",rc);
if ( rc == -EPIPE )
{
/* EPIPE means overrun */
fprintf(stderr,"overrun occured\n");
snd_pcm_prepare(handle);
}
else if ( rc < 0 )
{
fprintf(stderr,"error from read: %s\n",
snd_strerror(rc));
}
else if ( rc != (int)frames)
{
fprintf(stderr,"short read, read %d frames\n",rc);
}
// 将音频数据写入文件
rc = fwrite(buffer, 1, size, out_fd);
// rc = write(1, buffer, size);
if ( rc != size )
{
fprintf(stderr,"short write: wrote %d bytes\n",rc);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
fclose(out_fd);
}
其他(未验证):
//
// gcc recorder.c -o recorder -lasound
//#include "head4audio.h"
#include </usr/include/alsa/asoundlib.h>
// 根据本系统的具体字节序处理的存放格式
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define RIFF ('F'<<24 | 'F'<<16 | 'I'<<8 | 'R'<<0)
#define WAVE ('E'<<24 | 'V'<<16 | 'A'<<8 | 'W'<<0)
#define FMT (' '<<24 | 't'<<16 | 'm'<<8 | 'f'<<0)
#define DATA ('a'<<24 | 't'<<16 | 'a'<<8 | 'd'<<0)
#define LE_SHORT(val) (val)
#define LE_INT(val) (val)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define RIFF ('R'<<24 | 'I'<<16 | 'F'<<8 | 'F'<<0)
#define WAVE ('W'<<24 | 'A'<<16 | 'V'<<8 | 'E'<<0)
#define FMT ('f'<<24 | 'm'<<16 | 't'<<8 | ' '<<0)
#define DATA ('d'<<24 | 'a'<<16 | 't'<<8 | 'a'<<0)
#define LE_SHORT(val) bswap_16(val)
#define LE_INT(val) bswap_32(val)
#endif
#define DURATION_TIME 3
// 准备WAV格式参数
void prepare_wav_params(wav_format *wav)
{
wav->format.fmt_id = FMT;
wav->format.fmt_size = LE_INT(16);
wav->format.fmt = LE_SHORT(WAV_FMT_PCM);
wav->format.channels = LE_SHORT(2); // 声道数目
wav->format.sample_rate = LE_INT(44100); // 采样频率
wav->format.bits_per_sample = LE_SHORT(16); // 量化位数
wav->format.block_align = LE_SHORT(wav->format.channels
* wav->format.bits_per_sample/8);
wav->format.byte_rate = LE_INT(wav->format.sample_rate
* wav->format.block_align);
wav->data.data_id = DATA;
wav->data.data_size = LE_INT(DURATION_TIME
* wav->format.byte_rate);
wav->head.id = RIFF;
wav->head.format = WAVE;
wav->head.size = LE_INT(36 + wav->data.data_size);
}
// 设置WAV格式参数
void set_wav_params(pcm_container *sound, wav_format *wav)
{
// 1:定义并分配一个硬件参数空间
snd_pcm_hw_params_t *hwparams;
snd_pcm_hw_params_alloca(&hwparams);
// 2:初始化硬件参数空间
snd_pcm_hw_params_any(sound->handle, hwparams);
// 3:设置访问模式为交错模式(即帧连续模式)
snd_pcm_hw_params_set_access(sound->handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
// 4:设置量化参数
snd_pcm_format_t pcm_format = SND_PCM_FORMAT_S16_LE;
snd_pcm_hw_params_set_format(sound->handle,
hwparams, pcm_format);
sound->format = pcm_format;
// 5:设置声道数目
snd_pcm_hw_params_set_channels(sound->handle,
hwparams, LE_SHORT(wav->format.channels));
sound->channels = LE_SHORT(wav->format.channels);
// 6:设置采样频率
// 注意:最终被设置的频率被存放在来exact_rate中
uint32_t exact_rate = LE_INT(wav->format.sample_rate);
snd_pcm_hw_params_set_rate_near(sound->handle,
gcc read_microphone.c -o read_microphone -lasoundhwparams, &exact_rate, 0);
// 7:设置buffer size为声卡支持的最大值
snd_pcm_uframes_t buffer_size;
snd_pcm_hw_params_get_buffer_size_max(hwparams,
&buffer_size);
snd_pcm_hw_params_set_buffer_size_near(sound->handle,
hwparams, &buffer_size);
// 8:根据buffer size设置period size
snd_pcm_uframes_t period_size = buffer_size / 4;
snd_pcm_hw_params_set_period_size_near(sound->handle,
hwparams, &period_size, 0);
// 9:安装这些PCM设备参数
snd_pcm_hw_params(sound->handle, hwparams);
// 10:获取buffer size和period size
// 注意:他们均以 frame 为单位 (frame = 声道数 * 量化级)
snd_pcm_hw_params_get_buffer_size(hwparams,
&sound->frames_per_buffer);
snd_pcm_hw_params_get_period_size(hwparams,
&sound->frames_per_period, 0);
// 11:保存一些参数
sound->bits_per_sample =
snd_pcm_format_physical_width(pcm_format);
sound->bytes_per_frame =
sound->bits_per_sample/8 * wav->format.channels;
// 12:分配一个周期数据空间
sound->period_buf =
(uint8_t *)calloc(1,
sound->frames_per_period * sound->bytes_per_frame);
}
snd_pcm_uframes_t read_pcm_data(pcm_container *sound,
snd_pcm_uframes_t frames)
{
snd_pcm_uframes_t exact_frames = 0;
snd_pcm_uframes_t n = 0;
uint8_t *p = sound->period_buf;
while(frames > 0)
{
n = snd_pcm_readi(sound->handle, p, frames);
frames -= n;
exact_frames += n;
p += (n * sound->bytes_per_frame);
}
return exact_frames;
}
// 从PCM设备录取音频数据,并写入fd中
void recorder(int fd, pcm_container *sound, wav_format *wav)
{
// 1:写WAV格式的文件头
write(fd, &wav->head, sizeof(wav->head));
write(fd, &wav->format, sizeof(wav->format));
write(fd, &wav->data, sizeof(wav->data));
// 2:写PCM数据
uint32_t total_bytes = wav->data.data_size;
while(total_bytes > 0)
{
uint32_t total_frames =
total_bytes / (sound->bytes_per_frame);
snd_pcm_uframes_t n =
MIN(total_frames, sound->frames_per_period);
uint32_t frames_read = read_pcm_data(sound, n);
write(fd, sound->period_buf,
frames_read * sound->bytes_per_frame);
total_bytes -=
(frames_read * sound->bytes_per_frame);
}
}
int main(int argc, char **argv)
{
if(argc != 2)
{
printf("Usage: %s <wav-file>\n", argv[0]);
exit(1);
}
// 1:打开WAV格式文件
int fd = open(argv[1], O_CREAT|O_WRONLY|O_TRUNC, 0777);
// 2: 打开PCM设备文件
pcm_container *sound = calloc(1, sizeof(pcm_container));
//snd_pcm_open(&sound->handle, "default",SND_PCM_STREAM_CAPTURE, 0);
snd_pcm_open(&sound->handle, "hw:1,0",SND_PCM_STREAM_CAPTURE,0);
// 3: 准备并设置WAV格式参数
wav_format *wav = calloc(1, sizeof(wav_format));
prepare_wav_params(wav);
set_wav_params(sound, wav);
// 4: 开始从PCM设备"plughw:0,0"录制音频数据
// 并且以WAV格式写到fd中
recorder(fd, sound, wav);
// 5: 释放相关资源
snd_pcm_drain(sound->handle);
close(fd);
snd_pcm_close(sound->handle);
free(sound->period_buf);
free(sound);
free(wav);
return 0;
}