alsa录音,播放调试

本次调试基于全志v3s芯片linux系统,刚开始毫无头绪,即使都调试完成了,还是有很多地方不明白。
1、首先确保硬件和驱动都可以正常使用,用如下命令测试
录音测试

amixer cset numid=10,iface=MIXER,name='Audio main mic' 1
arecord -d 3 -f S16_LE -r 16000 tmp.wav

播放测试

amixer cset numid=1,iface=MIXER,name='Master Playback Volume' 63
amixer cset numid=17,iface=MIXER,name='Speaker Function' 0
aplay tmp.wav

如果没有这些命令,需要在编译文件系统时在buildroot里加进去,可以参考:https://blog.csdn.net/Jun626/article/details/100036595
如果配置完没有生成,就make clean再重新编译(我当时就是这个情况,配置了,没有生成)。
确定硬件和驱动没问题,就下一步。
2、通过编写程序读取音频流
2.1 准备工作
alsa的驱动框架,不像其他字符设备直接使用open、write、read等直接操作,而是需要alsa的动态库的API,来完成对alsa的参数配置,在上一步配置buildroot时会生成这个库,在编译时需要指定库和头文件的位置,也可以重新下载alsa-lib来自己编译,但是版本要和buildroot的alsa版本一样,要不然编译的程序和alsa相关命令不能同时使用。
alsa的编程指南看这里:https://blog.csdn.net/liuchen_csdn/article/details/52097088
官放说明看这里:https://users.suse.com/~mana/alsa090_howto.html#sect02
这里就直接上我改的代码:
这是一个录音后直接通过耳机播放的程序

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>



int main(void){
       /*Name of the PCM device ,like "default"*/
       char    *dev_name;
       int     rate = 44100;/*Sample rate*/
         int     exact_rate;/*Sample rate returned by*/
         int     dir;/*(1)exact_rate == rate --> dir=0,(2)exact_rate < rate --> dir=-1,(3)exact_rate > rate*/
         long unsigned int     periods = 2;/*Number of periods*/
         int     err;
         int     size;
         int     pcmreturn;
 //      snd_pcm_uframes_t       periodsize = 8192;
         snd_pcm_uframes_t       periodsize = 1764;
         snd_pcm_t       *play_handle;
         snd_pcm_t       *capture_handle;
         snd_pcm_stream_t        play_stream = SND_PCM_STREAM_PLAYBACK;
         snd_pcm_stream_t        capture_stream = SND_PCM_STREAM_CAPTURE;
         /*This structure contains information about */
         /*the hardware and can be used to specify the */
         /*configuration to be used for the PCM stream*/
         snd_pcm_hw_params_t     *hwparams;
         snd_pcm_hw_params_t     *c_hwparams;
         /*Init dev_name, Of course, later you will make this configure*/
         dev_name = strdup("default");
         /*Allocate the snd_pcm_hw_params_t structure on the stack*/
         snd_pcm_hw_params_alloca(&hwparams);
         snd_pcm_hw_params_alloca(&c_hwparams);
         FILE *out; 
         FILE *comm_fp_c = NULL;
         FILE *comm_fp_p = NULL;
 				 char comm_ret[100] = {'0'};
 				  				 
 				 /*shell command*/
 				  comm_fp_c = popen("amixer cset numid=10,iface=MIXER,name='Audio main mic' 1", "r");
 					if(comm_fp_c == NULL){
 						printf("shell error\n");
 						return 0;
 						}
 					while(fgets(comm_ret, sizeof(comm_ret) - 1, comm_fp_c) != NULL)
 						printf("amixer:\n%s\n", comm_ret);
 					memset(comm_ret, 0, sizeof(comm_ret));
 					pclose(comm_fp_c);
 				 
 				 
 					comm_fp_p = popen("amixer cset numid=17,iface=MIXER,name='Speaker Function' 0", "r");
 					if(comm_fp_p == NULL){
 						printf("shell error\n");
 						return 0;
 						}
 					while(fgets(comm_ret, sizeof(comm_ret) - 1, comm_fp_p) != NULL)
 						printf("amixer:\n%s\n", comm_ret);
 					pclose(comm_fp_p);
 					
         /* Open PCM. The last parameter of this function is the mode. */
         /* If this is set to 0, the standard mode is used. Possible   */
         /* other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.       */
         /* If SND_PCM_NONBLOCK is used, read / write access to the    */
         /* PCM device will return immediately. If SND_PCM_ASYNC is    */
         /* specified, SIGIO will be emitted whenever a period has     */
         /* been completely processed by the soundcard.                */
         if (snd_pcm_open(&play_handle, dev_name, play_stream, 0) < 0) {
                 fprintf(stderr, "Error opening PCM device %s\n", dev_name);
                 return(-1);
         }
         if (snd_pcm_open(&capture_handle, dev_name, capture_stream, 0) < 0) {
                 fprintf(stderr, "Error opening PCM device %s\n", dev_name);
                 return(-1);
         }
         /* Init hwparams with full configuration space */
         if (snd_pcm_hw_params_any(play_handle, hwparams) < 0) {
                 fprintf(stderr, "Can not configure this PCM device.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_any(capture_handle, c_hwparams) < 0) {
                 fprintf(stderr, "Can not configure this PCM device.\n");
                 return(-1);
         }
         /* Set access type. This can be either    */
         /* SND_PCM_ACCESS_RW_INTERLEAVED or       */
         /* SND_PCM_ACCESS_RW_NONINTERLEAVED.      */
         /* There are also access types for MMAPed */
         /* access, but this is beyond the scope   */
         /* of this introduction.                  */
         if (snd_pcm_hw_params_set_access(play_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
                 fprintf(stderr, "Error setting access.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_set_access(capture_handle, c_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
                 fprintf(stderr, "Error setting access.\n");
                 return(-1);
         }
         /* Set sample format */
         if (snd_pcm_hw_params_set_format(play_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
                 fprintf(stderr, "Error setting format.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_set_format(capture_handle, c_hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
                 fprintf(stderr, "Error setting format.\n");
                 return(-1);
         }
         exact_rate = rate;
         if (snd_pcm_hw_params_set_rate_near(play_handle, hwparams, &exact_rate, 0) < 0) {
                 fprintf(stderr, "Error setting rate.\n");
                 return(-1);
         }
         if (rate != exact_rate) {
                 fprintf(stderr, "The rate %d Hz is not supported by your hardware.\
                        ==> Using %d Hz instead.\n", rate, exact_rate);
         }
         exact_rate = rate;
         if (snd_pcm_hw_params_set_rate_near(capture_handle, c_hwparams, &exact_rate, 0) < 0) {
                 fprintf(stderr, "Error setting rate.\n");
                 return(-1);
         }
         if (rate != exact_rate) {
                 fprintf(stderr, "The rate %d Hz is not supported by your hardware.\
                        ==> Using %d Hz instead.\n", rate, exact_rate);
         }
 
         /* Set number of channels */
         if (snd_pcm_hw_params_set_channels(play_handle, hwparams, 2) < 0) {
                 fprintf(stderr, "Error setting channels.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_set_channels(capture_handle, c_hwparams, 2) < 0) {
                 fprintf(stderr, "Error setting channels.\n");
                 return(-1);
         }
 
         /* Set number of periods. Periods used to be called fragments. */
         if (snd_pcm_hw_params_set_periods(play_handle, hwparams, periods, 0) < 0) {
                 fprintf(stderr, "Error setting periods.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_set_periods(capture_handle, c_hwparams, periods, 0) < 0) {
                 fprintf(stderr, "Error setting periods.\n");
                 return(-1);
         }
         /* Set buffer size (in frames). The resulting latency is given by */
         /* latency = periodsize * periods / (rate * bytes_per_frame)     */
 //      if(snd_pcm_hw_params_get_buffer_size_max(hwparams,&periodsize) < 0)
 //      {
 //              fprintf(stderr,"Error get buffer size\n");
 //      }
 //      printf("periodsize=%lu\n",periodsize);
 
         if (snd_pcm_hw_params_set_buffer_size(play_handle, hwparams, (periodsize * periods)) < 0) {
                 fprintf(stderr, "Error setting buffersize.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params_set_buffer_size(capture_handle, c_hwparams, (periodsize * periods)) < 0) {
                 fprintf(stderr, "Error setting buffersize.\n");
                 return(-1);
         }
         /* Apply HW parameter settings to */
         /* PCM device and prepare device  */
         if (snd_pcm_hw_params(play_handle, hwparams) < 0) {
                 fprintf(stderr, "Error setting HW params.\n");
                 return(-1);
         }
         if (snd_pcm_hw_params(capture_handle, c_hwparams) < 0) {
                 fprintf(stderr, "Error setting HW params.\n");
                 return(-1);
         }
 //      snd_pcm_hw_params_free(hwparams);
 //      snd_pcm_hw_params_free(c_hwparams);
         if ((err = snd_pcm_prepare (play_handle)) < 0) {
                 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
                                  snd_strerror (err));
                 exit (1);
         }
         if ((err = snd_pcm_prepare (capture_handle)) < 0) {
                 fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
                                  snd_strerror (err));
                 exit (1);
         }
         snd_pcm_hw_params_get_period_size(c_hwparams, &periods,0);
         printf("periods=%d\n",periods);
         char *data_buf = (char*)malloc(periods);
         if(!data_buf){
                 fprintf(stderr, "Cannot malloc buffer for data\n");
         }
         
         while(1)
 
         {
                 pcmreturn = snd_pcm_readi( capture_handle, data_buf, periodsize);
                 if( pcmreturn == -EPIPE )
                 {
                         snd_pcm_prepare( capture_handle );
                         /*数据满了,没有被alsa读走*/
                         fprintf (stderr, "<<<<<<<<<<<<<<<<<<< Buffer Overrun >>>>>>>>>>>>>>>>>\n");
                         continue;
                 }else if( pcmreturn == -EBADFD ){
                         fprintf(stderr, "<<<<<<<<<<<<<<<<<<<< readi error -EBADFD >>>>>>>>>>>>>\n");
                         continue;
                 }else if( pcmreturn == -ESTRPIPE ){
                         fprintf(stderr, "<<<<<<<<<<<<<<<<<<<< readi error -ESTRPIPE >>>>>>>>>>>>>\n");
                 }else{
                         pcmreturn = snd_pcm_writei(play_handle, data_buf, pcmreturn);
                         if( pcmreturn == -EPIPE ){
                         	/*在播放例子中,如果应用程序写入数据到缓存区中的速度不够快,缓存区将会"饿死"。这样的错误被称 为"underrun"*/
                                 fprintf(stderr, "<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>\n");
                                 snd_pcm_prepare( play_handle );
                 //              continue;
                         }else if( pcmreturn == -ESTRPIPE ){
                                 fprintf(stderr, "<<<<<<<<<<<< writei error -ESTRPIPE >>>>>>>>>>>\n");
                         }else if( pcmreturn == -EBADFD ){
                                 fprintf(stderr, "<<<<<<<<<<<< writei error -EABDFD >>>>>>>>>>>>\n");
                         }
                 }
                 printf("pcmreturn = %d\n",pcmreturn);
 
 
         }

 
 }

在v3s上可以用,但在其他平台可能需要更改,如果需要单独录音或播放,可以把播放和录音分离出来。程序中open(“amixer cset numid=17,iface=MIXER,name=‘Speaker Function’ 0”, “r”);如果没有这一句,使用snd_pcm_writei播放录音时,只会读取前两帧,播放不了,这个命令的做用应该就是关闭扩音功能,从耳机输出。我在这里搞了好几才知道是这的问题。还要注意播放的音量是否为0。
int rate = 44100;/Sample rate/
snd_pcm_uframes_t periodsize = 1764;
当我把参数periodsize 设置为441时在本程序录音时,会出现Buffer Overrun 错误。但是在单独录音程序中没问题。这里似懂非懂的,还需要看看。
编译该程序的Makefile如下

SOURCE = $(wildcard *.c)
TARGETS = $(patsubst %.c, %, $(SOURCE))

CC = arm-buildroot-linux-gnueabihf-gcc
PREFIX=/usr/lib/alsa-lib
#CFLAGS = -Wall


all:$(TARGETS)

$(TARGETS):%:%.c
	$(CC) $<  -o $@ -I $(PREFIX)/include -L $(PREFIX)/lib -lasound



.PHONY:clean all
clean:
	-rm -rf $(TARGETS)

接下来要把获取到的音频流,实现speex编码后,然后speex解码。下一篇

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页