ALSA音频编程自我分析
2012-02-26 14:43
239 查看
http://cgy136652.blog.163.com/blog/static/361687282010268437218/
最近搞了一阵子的ALSA音频编程的东西,其实单是说其编程结构的确是比较简单的,相信很多朋友和我一样都在网络上搜索到很多相关资料,从ALSA的驱动-----》ALSA的lib库(提供了编程的API)-----》ALSA的utils,这三部分的确组成了ALSA开发中不可或缺的主要部分,同时ALSA项目的开放源码特性,使得开发者有更多的源码可以参考,其中ALSA的utils中提供的源码就是很好的材料。不过有了这些只是提供了能够参阅的资料,编程开发的事情,真的没有一定就能成,有些问题出来还是需要解决时间的。有些问题涉及到开发者自身的具体操作环境,所以很多问题出了还是不能在网上找到合适的答案,只有自己慢慢摸索。我从做些录放音小程序到时先语音的传输过程中,也曾面临着许多问题,但是要相信自己一定能解决并为之去查阅资料、去调试程序。所以出现问题是正常的,问题也是我们能解决问题的。总结一下这么久的工作,细节很多,但是有个问题在ALSA中很重要,很多问题就是由于这个问题导致的,那就是对音频文件的认识以及对该设备文件的参数设置。这个设置又涉及到硬件参数设置和软件参数设置。这个参数设置没对的话,很多莫名其妙的问题都会出来,让我们真的是摸不着头脑。同时也要说明的是同样的代码在不同的操作系统上会有不同的反应,这也是正常的,这就需要我们实时的调整了。这里就最普遍的参数设置问题给大家提出警示。不管是编写复杂程序还是编写测试小程序,只要参数设置正确无误了,问题就会少很多。因此,将aply.c文件中的设置参数的函数摘出来给分析一下,希望给继续做这方面的朋友做个分享,少走点我走过的弯路,同时也希望大家能够将自己的所学贡献出来,相互交流,共同进步!
下面首先大体介绍一下,接着会以注解的形式来重点分析我认为比较容易忽视,但是有很重要的参数设置。同时申明这些知识自己的浅薄一点认识,如果有不对的地方还请读者指正,不胜感激!
首先是硬件参数设置,如果设置好了硬件参数,做单方面的录音或放音是不会有大问题的,但是如果要涉及到同时的录放音交替,就必须好好的设置下软参数了,这样才能使得听起来的语音流畅。
说起来就这么简单的一点,但是要做好还是需要细致和不断尝试。很多实例程序源码是很值得参考的,至少在流程和结构上还比较规范。在懂得流程的基础上,我们再根据条件来调整细节,这样我们就有希望作出符合要求的程序来,希望做这一块的朋友,大家都合理作出更好的东西,期待ing!
最近搞了一阵子的ALSA音频编程的东西,其实单是说其编程结构的确是比较简单的,相信很多朋友和我一样都在网络上搜索到很多相关资料,从ALSA的驱动-----》ALSA的lib库(提供了编程的API)-----》ALSA的utils,这三部分的确组成了ALSA开发中不可或缺的主要部分,同时ALSA项目的开放源码特性,使得开发者有更多的源码可以参考,其中ALSA的utils中提供的源码就是很好的材料。不过有了这些只是提供了能够参阅的资料,编程开发的事情,真的没有一定就能成,有些问题出来还是需要解决时间的。有些问题涉及到开发者自身的具体操作环境,所以很多问题出了还是不能在网上找到合适的答案,只有自己慢慢摸索。我从做些录放音小程序到时先语音的传输过程中,也曾面临着许多问题,但是要相信自己一定能解决并为之去查阅资料、去调试程序。所以出现问题是正常的,问题也是我们能解决问题的。总结一下这么久的工作,细节很多,但是有个问题在ALSA中很重要,很多问题就是由于这个问题导致的,那就是对音频文件的认识以及对该设备文件的参数设置。这个设置又涉及到硬件参数设置和软件参数设置。这个参数设置没对的话,很多莫名其妙的问题都会出来,让我们真的是摸不着头脑。同时也要说明的是同样的代码在不同的操作系统上会有不同的反应,这也是正常的,这就需要我们实时的调整了。这里就最普遍的参数设置问题给大家提出警示。不管是编写复杂程序还是编写测试小程序,只要参数设置正确无误了,问题就会少很多。因此,将aply.c文件中的设置参数的函数摘出来给分析一下,希望给继续做这方面的朋友做个分享,少走点我走过的弯路,同时也希望大家能够将自己的所学贡献出来,相互交流,共同进步!
下面首先大体介绍一下,接着会以注解的形式来重点分析我认为比较容易忽视,但是有很重要的参数设置。同时申明这些知识自己的浅薄一点认识,如果有不对的地方还请读者指正,不胜感激!
首先是硬件参数设置,如果设置好了硬件参数,做单方面的录音或放音是不会有大问题的,但是如果要涉及到同时的录放音交替,就必须好好的设置下软参数了,这样才能使得听起来的语音流畅。
static void set_params(void) { snd_pcm_hw_params_t *params; snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t buffer_size; int err; size_t n; snd_pcm_uframes_t xfer_align; unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold;//设置软参数主要这两个参数要设置 snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_hw_params_any(handle, params); if (err < 0) { error(_("Broken configuration for this PCM: no configurations available")); exit(EXIT_FAILURE); } if (mmap_flag) { snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof()); snd_pcm_access_mask_none(mask); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX); err = snd_pcm_hw_params_set_access_mask(handle, params, mask); } else if (interleaved) err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); else err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED); if (err < 0) { error(_("Access type not available")); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_format(handle, params, hwparams.format); if (err < 0) { error(_("Sample format non available")); exit(EXIT_FAILURE); } err = snd_pcm_hw_params_set_channels(handle, params, hwparams.channels); if (err < 0) { error(_("Channels count non available")); exit(EXIT_FAILURE); } #if 0 err = snd_pcm_hw_params_set_periods_min(handle, params, 2); assert(err >= 0); #endif rate = hwparams.rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &hwparams.rate, 0); assert(err >= 0); if ((float)rate * 1.05 < hwparams.rate || (float)rate * 0.95 > hwparams.rate) { if (!quiet_mode) { char plugex[64]; const char *pcmname = snd_pcm_name(handle); fprintf(stderr, _("Warning: rate is not accurate (requested = %iHz, got = %iHz)\n"), rate, hwparams.rate); if (! pcmname || strchr(snd_pcm_name(handle), ':')) *plugex = 0; else snprintf(plugex, sizeof(plugex), "(-Dplug:%s)", snd_pcm_name(handle)); fprintf(stderr, _(" please, try the plug plugin %s\n"), plugex); } } rate = hwparams.rate; if (buffer_time == 0 && buffer_frames == 0) { err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); assert(err >= 0); if (buffer_time > 500000)//这是指具体的环形缓从区大小,自己认为是以字节为单位的 buffer_time = 500000; } if (period_time == 0 && period_frames == 0) { if (buffer_time > 0) period_time = buffer_time / 4; else period_frames = buffer_frames / 4; } if (period_time > 0) err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, 0); else err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_frames, 0); assert(err >= 0); if (buffer_time > 0) { err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, 0); } else { err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_frames); } assert(err >= 0); err = snd_pcm_hw_params(handle, params); if (err < 0) { error(_("Unable to install hw params:")); snd_pcm_hw_params_dump(params, log); exit(EXIT_FAILURE); } snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); snd_pcm_hw_params_get_buffer_size(params, &buffer_size); if (chunk_size == buffer_size) { error(_("Can't use period equal to buffer size (%lu == %lu)"), chunk_size, buffer_size); exit(EXIT_FAILURE); } snd_pcm_sw_params_current(handle, swparams); err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align); if (err < 0) { error(_("Unable to obtain xfer align\n")); exit(EXIT_FAILURE); } if (sleep_min) xfer_align = 1; err = snd_pcm_sw_params_set_sleep_min(handle, swparams, sleep_min); assert(err >= 0); if (avail_min < 0) n = chunk_size;//如果语音数据的长度小于这个值得话会播放不出来的 else n = (double) rate * avail_min / 1000000;//注意,将后面的数字强制转换为n的数据类型会减少出错 err = snd_pcm_sw_params_set_avail_min(handle, swparams, n); /* round up to closest transfer boundary */ n = (buffer_size / xfer_align) * xfer_align;//这部分就是软参数的设置了,主要设置了两个阈值,主要问题是注意数据类型的转换,设置合适的阈值,合适的阈值不是固定的,是一句实际情况来定的。 if (start_delay <= 0) { start_threshold = n + (double) rate * start_delay / 1000000; } else start_threshold = (double) rate * start_delay / 1000000; if (start_threshold < 1) start_threshold = 1; if (start_threshold > n) start_threshold = n; err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); assert(err >= 0); if (stop_delay <= 0) stop_threshold = buffer_size + (double) rate * stop_delay / 1000000; else stop_threshold = (double) rate * stop_delay / 1000000; err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); assert(err >= 0); err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align); assert(err >= 0); if (snd_pcm_sw_params(handle, swparams) < 0) { error(_("unable to install sw params:")); snd_pcm_sw_params_dump(swparams, log); exit(EXIT_FAILURE); } if (verbose) snd_pcm_dump(handle, log); bits_per_sample = snd_pcm_format_physical_width(hwparams.format); bits_per_frame = bits_per_sample * hwparams.channels; chunk_bytes = chunk_size * bits_per_frame / 8; audiobuf = realloc(audiobuf, chunk_bytes); if (audiobuf == NULL) { error(_("not enough memory")); exit(EXIT_FAILURE); } // fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size); }
说起来就这么简单的一点,但是要做好还是需要细致和不断尝试。很多实例程序源码是很值得参考的,至少在流程和结构上还比较规范。在懂得流程的基础上,我们再根据条件来调整细节,这样我们就有希望作出符合要求的程序来,希望做这一块的朋友,大家都合理作出更好的东西,期待ing!
相关文章推荐
- ALSA音频编程自我分析
- ALSA音频编程自我分析
- 原创:ALSA音频编程自我分析
- ALSA编程细节分析
- Alsa音频编程
- 【Linux&音频】Alsa音频编程【精华】
- Linux ALSA音频框架分析三:ALSA框架
- 6410音频ALSA驱动框架分析
- ALSA音频编程
- alsa音频编程
- [原创]ALSA 音频系统源代码分析: ALSA library 分析 --- 配置系统
- linux音频alsa驱动分析之三 解码器
- ALSA 音频系统源代码分析: ALSA library 分析 --- 配置系统
- linux嵌入式应用开发- ubuntu eclipse/c ALSA音频编程
- 优质搬砖【Linux&音频】Alsa音频编程
- ALSA编程细节分析
- alsa与oss音量设置例程, Linux音频编程指南
- Alsa音频应用编程
- ALSA编程细节分析
- Alsa音频编程【精华】