DSP

alsa数据编码mp3格式

2019-07-13 20:07发布

    在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 #include #include #include #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 ", 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 ", 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 "); snd_pcm_prepare(handle); }else if(ret == -EBADFD){ printf("PCM is not in the right state "); } else if(ret == -ESTRPIPE){ printf("stream is suspended and waiting for an application recovery "); } else if (ret < 0){ fprintf(stderr, "error from read: %s ",snd_strerror(ret)); } else if (ret != (int)frames){ fprintf(stderr, "short read, read %d frames ", ret); } else if (ret == 0){ printf(" pcm read 0 frame deta "); } 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 ", 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 ", 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格式