alsa数据编码mp3格式

    在linux系统中,有的系统没有dsp 设备文件,所以只能使用alsa提供的接口来读取声音设备。alsa 又许多的声卡和声卡驱动组成,它可以直接读取声卡采集到的数据。如同摄像头数据一般,采集到的原始数据都是都是非常大的,不利于我们正常存储和使用,所以出现了很多的压缩方法。本文采用的是国内使用最广的mp3压缩方式。先上代码:

/*=============================================================================  
#     FileName: pcm_encoder_mp3.c  
#         Desc: use lame encode pcm data to mp3 format, the pcm data 
#				read from alsa  
#       Author: licaibiao  
#   LastChange: 2017-03-27   
=============================================================================*/ 
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <lame/lame.h>
#define INBUFSIZE 128
#define MP3BUFSIZE (int) (1.25 * INBUFSIZE) + 7200

lame_global_flags	*gfp;
short				*input_buffer;
char 				*mp3_buffer;
char 				*outPath = "out.mp3";
FILE				*infp;
FILE				*outfp;

snd_pcm_hw_params_t *params;
snd_pcm_uframes_t   frames;
snd_pcm_t 			*handle;
int 				size;
short 				*alsa_buffer;

void lame_init_set(void)
{
	int ret_code;
    gfp = lame_init();
    if (gfp == NULL) 
    {
          printf("lame_init failed/n");
    }

    ret_code = lame_init_params(gfp);
    if (ret_code < 0)
    {
         printf("lame_init_params returned %d/n",ret_code);
    }
    outfp = fopen(outPath, "wb");	
}

void lame_alloc_buffer(void)
{
	input_buffer = (short*)malloc(INBUFSIZE*2);
    mp3_buffer = (char*)malloc(MP3BUFSIZE);	
}

void lame_release(void)
{
    free(mp3_buffer);
    free(input_buffer);
    fclose(outfp);
    lame_close(gfp);
}

void alsa_init(void){
	unsigned int 		val;
	int dir;
	int ret;

	ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
	if (ret < 0) {
    	fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(ret));
   		 exit(1);
	}

	snd_pcm_hw_params_alloca(¶ms);
	snd_pcm_hw_params_any(handle, 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, 2);
	val = 44100;
	snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
	frames = 32;
	snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
	ret = snd_pcm_hw_params(handle, params);
	if (ret < 0) {
    	fprintf(stderr,"unable to set hw parameters: %s\n", snd_strerror(ret));
    	exit(1);
	}
}

void alsa_alloc_buffer(void){
	int dir;

	snd_pcm_hw_params_get_period_size(params, &frames, &dir);
	size = frames * 4; 
	alsa_buffer = (short *) malloc(size);
	
}

void alsa_release(void){
	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	free(alsa_buffer);
}
void lame_encode(void) 
{
	int input_samples;
	int mp3_bytes;
	int status = 0;
	int ret = 0;
	int loop = 10000;

	while(loop--){
		ret = snd_pcm_readi(handle, alsa_buffer, frames);
		if (ret == -EPIPE){
			fprintf(stderr, "overrun occurred\n");
			snd_pcm_prepare(handle);
		}else if(ret == -EBADFD){
			printf("PCM is not in the right state \n");
		}
		else if(ret == -ESTRPIPE){
			printf("stream is suspended and waiting for an application recovery \n");
		}
		 else if (ret < 0){
			fprintf(stderr, "error from read: %s\n",snd_strerror(ret));
		}
		 else if (ret != (int)frames){
			 fprintf(stderr, "short read, read %d frames\n", ret);
		}
		else if (ret == 0){
			printf(" pcm read 0 frame deta \n ");
		}

	    memcpy(input_buffer, alsa_buffer, size);
		mp3_bytes = lame_encode_buffer_interleaved(gfp, input_buffer, size/4, mp3_buffer, MP3BUFSIZE);
		if (mp3_bytes < 0) 
		{
			  printf("lame_encode_buffer_interleaved returned %d \n", mp3_bytes);
		} 
		else if(mp3_bytes > 0) 
		{
			 fwrite(mp3_buffer, 1, mp3_bytes, outfp);
		}
	
	}

	mp3_bytes = lame_encode_flush(gfp, mp3_buffer, sizeof(mp3_buffer));
	if (mp3_bytes > 0) 
	{
		  printf("writing %d mp3 bytes\n", mp3_bytes);
		  fwrite(mp3_buffer, 1, mp3_bytes, outfp);
	}
}

int main(int argc, char** argv)
{
	lame_init_set();
	lame_alloc_buffer();
	alsa_init();
	alsa_alloc_buffer();
	lame_encode();
	lame_release();
	alsa_release();
}


    程序是通过alsa采集pcm接口的声音原始数据,然后使用LAME 编码库把pcm 的原始数据编码成MP3文件。

Makefile 文件:

APP = test
LINK_LIBS = -lmp3lame -lasound
LaINK_OPTS = -lm	
LINK_OBJ  = pcm_encoder_mp3.o

C         = c
OBJ       = o
C_FLAGS	  =  $(CPPFLAGS) $(CFLAGS)

.$(C).$(OBJ):  
	cc -c -g $(C_FLAGS)  $<  


$(APP): $(LINK_OBJ)  
	gcc -o  $@  $(LINK_OBJ) $(LINK_LIBS)  $(LINK_OPTS)

clean:
	rm -rf *o $(APP) *mp3 


    在Makefile文件中需要注意,需要链接alsa和LAME的相应的库-lmp3lame -lasound 。

    上面的程序在运行之前,需要确定已经正确安装了ALSA库和LAME库。可以参考 linux 音频子系统学习及软件安装
    编译运行上面的代码生成out.mp3文件,可以通过GoldWave.exe 工具查看音频情况。


    调试注意事项:

    1.使用Audacity  或是GoldWave工具查看编码后的数据,如果发现边长或是缩短了一倍或是几倍,那么需要查看mp3_bytes = lame_encode_buffer_interleaved(gfp, input_buffer, size/4, mp3_buffer, MP3BUFSIZE); 中的第三个参数是否设置正确。

    2.LAME 编码库的输入buffer 不能直接使用ALSA 的输出buffer。在我的测试中发现会出现异常,所以我独立申请了buffer,采集到数据后再拷贝一次。


    遗留问题:

    在使用ALSA采集数据的时候,snd_pcm_readi 读取数据的时候会出现采集不到数据的问题。它采集不到数据返回值并没有返回异常,而是返回0。出现该问题需要重新打开设备再读取数据。至于这个问题是由何引起,具体还没有定位。这里做个记录,以后有时间再定位。


    这里再提供一个ALSA采集数据和LAME 编码的一个应用实例。可以在这里下载: alsa数据编码mp3格式



已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页