• 【音视频|ALSA】基于alsa-lib开发ALSA应用层程序--附带源码


    😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
    🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
    🤣本文内容🤣:🍭基于alsa-lib开发ALSA应用层程序🍭
    😎金句分享😎:🍭盖士人读书,第一要有志,第二要有识,第三要有恒。有志则断不甘为下流,有识则知学问无尽,不敢以一得自足,如河伯之观海,如井蛙之窥天,皆无识者也。有恒则断无不成之事,此三者缺一不可。 ——《曾国藩家书》🍭

    【音视频|ALSA】ALSA是什么?ALSA框架详细介绍
    【音视频|ALSA】SS528开发板编译Linux内核ALSA驱动、移植alsa-lib、采集与播放usb耳机声音


    在这里插入图片描述

    一、ALSA应用层开发基础知识

    • sample:样本,采样点。数字音频最小单位,其大小与位宽有关,一般为8bit(1个字节)、16bit(2个字节);
    • channel:声道,一般单声道(mono)和立体声(stereo),还有一些多声道如5.1声道。
    • frame:帧,一个完整的声音单元,即单次采样的所有声道的数据。frame=sample*channel
      例如:48Khz、16位的立体声PCM流的1帧是4个字节。
    • sample rate:采样率,即每秒的采样次数。如果采样率为48kHz,则说明一秒采样48000帧。
    • period size:周期大小,是每次硬件中断之间的帧数。
    • buffer size:缓冲区大小,必须大于一个周期的大小。一般为周期大小的2倍。单位也是帧数。

    在这里插入图片描述

    例子:
    结合上面的知识点,这里以48kHz、16bit的立体声音频流举例:

    • 16bit则每个样本为2个字节,
    • 立体声表示有2个声道,
    • 48kHz 表示每秒有48000个音频帧。

    由此可以计算出每秒钟传输的数据大小:2 * 2 * 48000=192000字节;

    现在如果ALSA每秒钟产生一个硬件中断,在每秒结束时,我们需要准备好192000字节;
    如果它每半秒中断一次,对于同一个流,我们需要在每次中断时准备好192000/2 = 96000字节;
    如果每100毫秒发生一次中断,我们需要在每次中断时准备好192000*(0.1/1) = 19200字节。

    我们可以通过设置周期大小(以帧为单位)来控制PCM中断的产生时间。
    如果我们将48kHz、16bit的立体声音频流的period size设置为4800帧(也就是480022=19200字节),则每19200字节就会产生一个中断,也就是100ms。
    相应地,buffer size至少应为2*period_size = 2*4800= 9600帧(960022 = 38400字节)。

    实际编程中,可能需要计算一个周期的总字节数period bytes,就是等于period size乘以每一帧的大小。同样的,buffer的总字节数buffer bytes等于buffer size乘以一帧大小。

    如果已知音频的采样率、通道数、位宽、周期数,则buffer sizebuffer timeperiod sizeperiod time 这四个值可以相互推断出来:
    以48000Hz采样率、2声道、16bit、4周期来举例,这样的音频流一秒钟的帧数是48000帧,如果buffer size是48000,则buffer time刚好就是一秒;如果buffer time是500ms,则buffer size是24000帧。
    period size=buffer size/周期数period time=buffer time/周期数

    在这里插入图片描述

    二、alsa-lib常用函数

    alsa-lib的函数声明在pcm.h,总共可以分为16个模块:

    • PCM Interface
    • Stream Information
    • Hardware Parameters
    • Software Parameters
    • Access Mask Functions
    • Format Mask Functions
    • Subformat Mask Functions
    • Status Functions
    • Description Functions
    • Debug Functions
    • Direct Access (MMAP) Functions
    • Helper Functions
    • Hook Extension
    • Scope Plugin Extension
    • Simple setup functions
    • Deprecated Functions

    可以在官方文档查看对应的模块函数说明:
    https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
    在这里插入图片描述

    下表介绍一些常用的函数:

    函数说明
    snd_pcm_open根据pcm设备名字打开一个pcm设备
    snd_pcm_hw_params_malloc使用标准malloc分配无效的snd_pcm_hw_params_t
    snd_pcm_hw_params_any用PCM的完整配置空间填充参数。
    snd_pcm_hw_params_set_access将配置空间限制为仅包含一种访问类型。
    snd_pcm_hw_params_set_format将配置空间限制为仅包含一种格式。
    snd_pcm_hw_params_set_channels将配置空间限制为仅包含一个通道计数。
    snd_pcm_hw_params_set_rate_near将配置空间限制为具有最接近目标的速率。
    snd_pcm_hw_params_get_buffer_time_max从配置空间中提取最大缓冲时间。
    snd_pcm_stream获取PCM句柄的流
    snd_pcm_hw_params_set_buffer_time_near限制配置空间以使缓冲时间最接近目标。
    snd_pcm_hw_params_set_period_time_near限制配置空间以使周期时间最接近目标。
    snd_pcm_hw_params安装从配置空间中选择的一个PCM硬件配置,并调用snd_pcm_prepare。
    snd_pcm_nonblock设置非阻塞模式
    snd_pcm_hw_params_get_period_size从配置空间中提取周期大小。
    snd_pcm_hw_params_get_buffer_size从配置空间中提取周期大小。
    snd_pcm_format_physical_width返回存储PCM样本所需的位。

    更多函数说明参考:
    https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html
    https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
    在这里插入图片描述

    三、编写ALSA应用层程序

    这小节介绍简单的ALSA应用程序开发流程,以及给出例子源码。

    在编写代码前,可以先使用cat /proc/asound/card0/stream0查看ALSA设备支持的参数:

    # cat /proc/asound/card0/stream0 
    Rapoo Gaming Headset at usb-10300000.xhci_0-1.1, full speed : USB Audio
    
    Playback:
      Status: Stop
      Interface 1
        Altset 1
        Format: S16_LE
        Channels: 2
        Endpoint: 1 OUT (ADAPTIVE)
        Rates: 48000, 44100
    
    Capture:
      Status: Stop
      Interface 2
        Altset 1
        Format: S16_LE
        Channels: 1
        Endpoint: 2 IN (ASYNC)
        Rates: 48000, 44100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Playback:播放设备
    Capture:录音设备
    Interface:接口序号
    Format:格式
    Channels:通道数
    Rates:支持的采样率

    3.1 alsa播放程序开发–alsa-playback.c

    开发流程:

    • 1、打开设备:调用 snd_pcm_open,指定类型为SND_PCM_STREAM_PLAYBACK,以及设备名称,打开设备;
    • 2、设置硬件参数
      设置存取方式、格式、通道数、采样率、缓冲时间、周期时间,最后将参数写入设备;
      如果有一些参数不清楚怎么设置,可以使用命令cat /proc/asound/card0/stream0查看支持的参数:
    • 3、播放音频
      每次往alsa驱动写入一个周期大小的字节,不足一周期的要填0;
    • 4、释放资源,关闭设备

    下面是一个非常简单的ALSA播放音频的代码,复制后保存为alsa-playback.c,使用命令aarch64-mix210-linux-gcc alsa-playback.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-playback 已编译通过。

    48000Hz-16bit-2ch-ChengDu.pcm 文件下载:https://download.csdn.net/download/wkd_007/88421282

    // alsa-playback.c
    // aarch64-mix210-linux-gcc alsa-playback.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-playback
    
    /*
    * snd_pcm_hw_params_alloca 申请的内存在函数返回后会自动释放,不需要手动释放。这个函数会在栈上分配一块内存,函数返回后,栈上的内存会自动被回收。
    */
    #include 
    #include 
    
    #define PCM_NAME	"hw:0,0"
    #define PLAYBACK_FILE "48000Hz-16bit-2ch-ChengDu.pcm"
    
    snd_pcm_hw_params_t *hw_params;
    static unsigned int rate = 48000;           /* stream rate */
    
    int set_hw_params(snd_pcm_t *handle, int format, int channels, snd_pcm_uframes_t *period_frames)
    {
    	int err = -1;
        // 分配硬件参数空间,调用 alloca 在栈分配内存,函数结束后自动释放,不需要调用 snd_pcm_hw_params_free
        snd_pcm_hw_params_alloca(&hw_params);
    
        //1、以默认值填充硬件参数
        if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
            return err;
        }
    
        //2、 Restrict a configuration space to contain only real hardware rates.
        if ((err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0)) < 0) {
            return err;
        }
    
        //3、设置存取方式为交叉存储
        if ((err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
            return err;
        }
    
        //4、设置格式,S16_LE等
        if ((err = snd_pcm_hw_params_set_format(handle, hw_params, format)) < 0) {
            return err;
        }
    
        //5、设置通道
        if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0) {
            return err;
        }
    
        //6、大致设置采样率
        unsigned int rrate;
        rrate =rate;
        if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, NULL)) < 0) 	   {
            return err;
        }
    	
        //7、设置缓冲时间
    	unsigned int buffer_time, period_time;
    	// 先获取缓存时间
    	if((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time, 0))<0){
    		return err;
    	}
    	
    	if (buffer_time > 500000){
    		buffer_time = 500000; // 500ms读取完整个buffer,结合下面代码一个周期就是 buffer_time/4=125ms,每个周期会产生一个中断
    		printf("[%s %d] buffer_time=%d, irq=%d\n",__FILE__,__LINE__,buffer_time, buffer_time/4);
    	}
    
    	// 设置缓冲时间
    	if ((err = snd_pcm_hw_params_set_buffer_time_near(handle, hw_params, &buffer_time, 0)) < 0) {
    		return err;
    	}
        
    	// 8、设置周期时间,也就是中断时间
    	period_time = buffer_time / 4;
    	if ((err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &period_time, 0)) < 0) {
    		return err;
    	}
    
        // 9、将参数写入设备
        if ((err = snd_pcm_hw_params(handle, hw_params)) < 0){
            return err;
        }
    
        snd_pcm_uframes_t buffer_frames;
        snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_frames);
    
        if(period_frames != NULL) {
    		//获取一个周期有多少帧数据
    		if((err =snd_pcm_hw_params_get_period_size(hw_params, period_frames, 0)) < 0){
    			printf("cannot get period size (%s)\n", snd_strerror(err));
    			return err;
    		}
    	}
    	
    	if(err = (snd_pcm_nonblock(handle, 1) < 0)){
    		return err;
    	}
    	
    	// 10、释放 snd_pcm_hw_params_malloc 分配的内存
    	//snd_pcm_hw_params_free(hw_params);
    
        return 0;
    }
    
    int main()
    {
    	int err = -1;
    	snd_pcm_t *playback_handle;
    	snd_pcm_uframes_t period_frames; // 一周期的帧数
    	
    	// 1、打开设备
    	if((err = snd_pcm_open(&playback_handle, PCM_NAME, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
    		printf("cannot snd_pcm_open (%s)\n",snd_strerror(err));
    		return -1;
    	}
    	
    	// 2、设置硬件参数
    	set_hw_params(playback_handle, SND_PCM_FORMAT_S16_LE, 2, &period_frames);
    	
    	// 3、播放音频
    	// 3.1 打开pcm文件
    	int fd = open(PLAYBACK_FILE,O_RDONLY,0644);
    	if(fd < 0){
    		printf("open %s error!!!\n",PLAYBACK_FILE);
    		return -1;
    	}
    	
    	// 3.2 获取一周期的字节数
    	const int period_bytes = snd_pcm_frames_to_bytes(playback_handle,period_frames);
    	char *playback_buf = malloc(period_bytes);
    	
    	// 3.3 循环播放音频
    	int readframes = 0;
    	while(readframes = read(fd, playback_buf, period_bytes)) {
    		//解决最后一个周期数据问题
    		if(readframes < period_bytes) {
    			memset(playback_buf+readframes, 0, period_bytes-readframes);
    		}
    		
    		//向PCM写入数据,播放
    		err = snd_pcm_writei(playback_handle, playback_buf, period_frames);
    		if(err == -EPIPE) {
    			snd_pcm_prepare(playback_handle);
    			fprintf(stderr, "<<< snd_pcm_writei --> Buffer Underrun >>> \n");
    			err = snd_pcm_writei(playback_handle, playback_buf, period_frames);
    			if(err != period_frames) {
    				printf("write to audio interface failede err:%d (period_frames:%d)\n",err,period_frames);
    				break;
    			}
    		}
    		else if(err != period_frames) {
    			printf("write to audio interface failede err:%d (period_frames:%d)\n",err,period_frames);
    			break;
    		}
    		//printf("process:playback wrote %d frames\n",period_frames);
    		//usleep(100*1000);
    		usleep(130*1000); //测试用,超过 125ms,会报错 Underrun
    	}
    	
    	// 4.释放资源,关闭设备
    	free(playback_buf);
    	close(fd);
    	snd_pcm_close(playback_handle);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164

    3.2 alsa录制音频程序开发–alsa-capture.c

    开发流程:

    • 1、打开设备:调用 snd_pcm_open,指定类型为SND_PCM_STREAM_CAPTURE,以及设备名称,打开设备;
    • 2、设置硬件参数
      设置存取方式、格式、通道数、采样率、缓冲时间、周期时间,最后将参数写入设备;
      如果有一些参数不清楚怎么设置,可以使用命令cat /proc/asound/card0/stream0查看支持的参数:
    • 3、读取音频
      每次从alsa驱动读取一个周期大小的字节;
    • 4、释放资源,关闭设备
    // alsa-capture.c
    // aarch64-mix210-linux-gcc alsa-capture.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-capture
    
    /*
    * snd_pcm_hw_params_alloca 申请的内存在函数返回后会自动释放,不需要手动释放。这个函数会在栈上分配一块内存,函数返回后,栈上的内存会自动被回收。
    */
    #include 
    #include 
    
    #define PCM_NAME	"hw:0,0"
    #define CAPTURE_FILE "alsa-capture.pcm"
    
    snd_pcm_hw_params_t *hw_params;
    static unsigned int rate = 48000;           /* stream rate */
    
    int set_hw_params(snd_pcm_t *handle, int format, int channels, snd_pcm_uframes_t *period_frames)
    {
    	int err = -1;
        // 分配硬件参数空间,调用 alloca 在栈分配内存,函数结束后自动释放,不需要调用 snd_pcm_hw_params_free
        snd_pcm_hw_params_alloca(&hw_params);
    
        //1、以默认值填充硬件参数
        if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {
            return err;
        }
    
        //2、 Restrict a configuration space to contain only real hardware rates.
        if ((err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0)) < 0) {
            return err;
        }
    
        //3、设置存取方式为交叉存储
        if ((err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
            return err;
        }
    
        //4、设置格式,S16_LE等
        if ((err = snd_pcm_hw_params_set_format(handle, hw_params, format)) < 0) {
            return err;
        }
    
        //5、设置通道
        if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0) {
            return err;
        }
    
        //6、大致设置采样率
        unsigned int rrate;
        rrate =rate;
        if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, NULL)) < 0) 	   {
            return err;
        }
    	
        //7、设置缓冲时间
    	unsigned int buffer_time, period_time;
    	// 先获取缓存时间
    	if((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time, 0))<0){
    		return err;
    	}
    	
    	if (buffer_time > 500000){
    		buffer_time = 500000; // 500ms写完整个buffer,结合下面代码一个周期就是 buffer_time/4=125ms,每个周期会产生一个中断
    		printf("[%s %d] buffer_time=%d, irq=%d\n",__FILE__,__LINE__,buffer_time, buffer_time/4);
    	}
    
    	// 设置缓冲时间
    	if ((err = snd_pcm_hw_params_set_buffer_time_near(handle, hw_params, &buffer_time, 0)) < 0) {
    		return err;
    	}
        
    	// 8、设置周期时间,也就是中断时间
    	period_time = buffer_time / 4;
    	if ((err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &period_time, 0)) < 0) {
    		return err;
    	}
    
        // 9、将参数写入设备
        if ((err = snd_pcm_hw_params(handle, hw_params)) < 0){
            return err;
        }
    
        snd_pcm_uframes_t buffer_frames;
        snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_frames);
    
        if(period_frames != NULL) {
    		//获取一个周期有多少帧数据
    		if((err =snd_pcm_hw_params_get_period_size(hw_params, period_frames, 0)) < 0){
    			printf("cannot get period size (%s)\n", snd_strerror(err));
    			return err;
    		}
    	}
    	
    	if(err = (snd_pcm_nonblock(handle, 1) < 0)){
    		return err;
    	}
    	
    	// 10、释放 snd_pcm_hw_params_malloc 分配的内存
    	//snd_pcm_hw_params_free(hw_params);
    
        return 0;
    }
    
    int main()
    {
    	int err = -1;
    	snd_pcm_t *capture_handle;
    	snd_pcm_uframes_t period_frames; // 一周期的帧数
    	
    	// 1、打开设备
    	if((err = snd_pcm_open(&capture_handle, PCM_NAME, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
    		printf("cannot snd_pcm_open (%s)\n",snd_strerror(err));
    		return -1;
    	}
    	
    	// 2、设置硬件参数
    	set_hw_params(capture_handle, SND_PCM_FORMAT_S16_LE, 1, &period_frames);
    	
    	// 3、获取音频
    	// 3.1 打开录制文件
    	int fd = open(CAPTURE_FILE,O_RDWR | O_TRUNC | O_CREAT,0644);
    	if(fd < 0){
    		printf("open %s error!!!\n",CAPTURE_FILE);
    		return -1;
    	}
    	
    	// 3.2 获取一周期的字节数
    	const int period_bytes = snd_pcm_frames_to_bytes(capture_handle,period_frames);
    	char *capture_buf = malloc(period_bytes);
    	
    	int count = 100; // 捕获100个周期
    	int readframes = 0;
    	while(count--) {
    		//向PCM读一周期数据
    		memset(capture_buf,0,period_bytes);
    		if((readframes = snd_pcm_readi(capture_handle, capture_buf, period_frames)) < 0) {
    			if(readframes == -EPIPE)
    				printf("read from audio interface failed (%d), overrun, Need to read faster\n",readframes);
    			else
    				printf("read from audio interface failed (%d)\n",readframes);
    			break;
    		}
    		printf("--process:capture read %d frames\n",readframes);
    		write(fd,capture_buf,snd_pcm_frames_to_bytes(capture_handle,readframes));
    		usleep(100*1000); 
    		//usleep(130*1000); //测试用,超过 125ms,会报错 overrun
    	}
    	
    	// 4、释放资源,关闭设备
    	free(capture_buf);
    	close(fd);
    	snd_pcm_close(capture_handle);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    在这里插入图片描述

    四、XRUN( underrun和overrun)

    在 ALSA 数据传输中,最容易出现的错误是 underrun 和 overrun。

    • underrun:pcm 播放的时候,接口 snd_pcm_writei 返回 -EPIPE,为 underrun(不足)
      出现这问题原因是应用准备的音频数据不够,比如,驱动需要播放需要 1026 帧数据,但应用只准备好了 1024 帧。可以根据采样率和buffer sizeperiod size去调整;
    • overrun:录制音频的时候, 接口 snd_pcm_readi 返回 -EPIPE, 为 overrun(超载)
      alsa驱动一直往buffer里面写,但应用程序却读取的很慢,例如:驱动写了1026帧,而应用层只读取了1024帧。需要加快读取速度。或者调整buffer sizeperiod size

    在这里插入图片描述

    五、总结

    文章介绍了alsa的基础知识,以及基于alsa-lib开发ALSA应用层程序的开发流程和alsa开发过程钟常见的报错,提供了简单的alsa应用层代码。


    参考资料:
    ALSA官网资料——FramesPeriods:https://alsa-project.org/main/index.php/FramesPeriods
    【Linux&音频】Alsa音频编程【精华】:https://blog.csdn.net/u012183924/article/details/53407668
    ALSA 音频数据传输 underrun 和 overrun:https://blog.csdn.net/qq_38350702/article/details/111995039
    Linux应用开发【第八章】ALSA应用开发:https://blog.csdn.net/thisway_diy/article/details/121809633

    在这里插入图片描述
    如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

  • 相关阅读:
    AWS云上面的k8s统一日志收集(Fluent Bit+EKS+CW)
    一篇五分生信临床模型预测文章代码复现——Figure 3. 基因富集分析(一)
    【NLP】第12章 检测客户情绪以做出预测
    软件测试,面试题——水杯测试用例
    Vue03/Vue指令、v-for的key说明、Vue 就地复用策略
    SVN 服务器建立
    广州华锐互动:VR模拟高楼层建筑应急逃生,提供身临其境的虚拟体验
    ssh免密登录&传输
    国产分布式数据库sequoiadb的前景如何?
    QT--day3
  • 原文地址:https://blog.csdn.net/wkd_007/article/details/133859810