编译:

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(&params);
   /* 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;
}

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐