当前位置:首页 > 芯闻号 > 充电吧
[导读]作者: Matthias Nagorni 译者: Kevin Lei 转载请注明出处:http://kevinlei.cublog.cn 1. 介绍 这个HOWTO计划提供一个简短的介绍,使用ALSA

作者: Matthias Nagorni
译者: Kevin Lei
转载请注明出处:http://kevinlei.cublog.cn

1. 介绍

这个HOWTO计划提供一个简短的介绍,使用ALSA 0.9.0写一个简单的音频应用程序.

Section2解释了PCM音频最基本的函数.如果你删除其中的解释文本,那么最后你会得到一个极小的PCM回放程序. Section3简短地讨论了一些PCM截获函数.

在Section4你将学习怎么为ALSA音序器写一个简单的客户端.这一章节基于seqdemo.c这个例子,一个可以接收MIDI事件并且表示大多数重要的事件类型的程序.Section5演示ALSAMIDI音序器怎样被用于"路由"MIDI事件,从一个输入端口到一些输出端口.这一段基于midiroute.c这个例子.

Section6使PCM回放和MIDI输入联结起来,并且解释了迷你合成器--miniFMsynth.c.这一章节引入基于回调的音频回放,就像PaulDavis在linux-audio-dev邮件列表里建议的那样.Section7对基于ALSA音序器队列的MIDI调度提供一个短小的介绍,基于miniArp.c这个例子.

推荐你也看看doxygen生成的ALSA库函数参考.

编译一个ALSA应用程序:

只要用-lasound参数并且确保你已包含了
#include

2. 基本PCM音频

为ALSA 0.9.0写一个简单的PCM应用程序我们首先需要一个PCM设备的句柄.然后我们必须指定同时可供回放或截获的PCM流的方向.我们同样必须提供一些关于我们想要使用的设置选项的信息,比如缓冲区大小,采样率,PCM数据格式等.所以,首先我们声明:
  
    /* Handle for the PCM device */ 
    snd_pcm_t *pcm_handle;          

    /* Playback stream */
    snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;

    /* 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;
  
最重要的ALSA PCM设备接口是"plughw"和"hw"接口.如果你使用"plughw"接口,你不需要很在意声卡硬件.如果你的声卡不支持你指定的采样率或采样格式,你的数据将自动被转换.这同样适用于声道的访问类型和号码.对于"hw"接口,你必须检查你的硬件是否支持你想要使用的设置选项.

    /* Name of the PCM device, like plughw:0,0          */
    /* The first number is the number of the soundcard, */
    /* the second number is the number of the device.   */
    char *pcm_name;
  
然后我们初始化变量并分配一个hwparams结构:

    /* Init pcm_name. Of course, later you */
    /* will make this configurable ;-)     */
    pcm_name = strdup("plughw:0,0");
  

    /* Allocate the snd_pcm_hw_params_t structure on the stack. */
    snd_pcm_hw_params_alloca(&hwparams);
  
现在我们可以打开PCM设备:

    /* 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(&pcm_handle, pcm_name, stream, 0) < 0) {
      fprintf(stderr, "Error opening PCM device %sn", pcm_name);
      return(-1);
    }
  
在我们可以往声卡写PCM数据之前,我们必须指定访问类型,采样格式,采样率,声道号码,周期数目以及周期大小.首先,我们以声卡的全部设置选项空间来初始化hwparams结构.

    /* Init hwparams with full configuration space */
    if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
      fprintf(stderr, "Can not configure this PCM device.n");
      return(-1);
    }
  
Information about possible configurations can be obtained with a set of functions named
关于合适的设置选项的信息,能以函数命名的一个集合获得.

    snd_pcm_hw_params_can_
    snd_pcm_hw_params_is_
    snd_pcm_hw_params_get_
  
绝大多数重要的参数的可用性,换句话说,访问类型,缓冲区大小,声道号码,采样格式,采样率,以及周期数目,可以以函数命名的一个集合来检验.

    snd_pcm_hw_params_test_ 

如果"hw"接口被使用,这些查询函数尤其重要.设置选项空间能以一个函数命名的集合限制在某一设置选项

    snd_pcm_hw_params_set_
  
例如,我们假设声卡可以被设置为16位,Little Endian数据的立体声回放,44100Hz采样率.相应地,我们限制设置选项空间匹配于这个设置选项:

    int rate = 44100; /* Sample rate */
    int exact_rate;   /* Sample rate returned by */
                      /* snd_pcm_hw_params_set_rate_near */ 
    int dir;          /* exact_rate == rate --> dir = 0 */
                      /* exact_rate < rate  --> dir = -1 */
                      /* exact_rate > rate  --> dir = 1 */
    int periods = 2;       /* Number of periods */
    snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
  
访问类型指定了哪一个多声道数据储存在缓冲区的方法.对于交错访问,缓冲区里的每一个帧为声道容纳连续的采样数据.对于16位立体声数据,这意味着缓冲区以字为单位为左右声道交错地容纳采样数据.对于非交错访问,每一个周期为第一个声道容纳所有采样数据接着是第二个声道的采样数据.
  
    /* 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(pcm_handle, 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(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) {
      fprintf(stderr, "Error setting format.n");
      return(-1);
    }

    /* Set sample rate. If the exact rate is not supported */
    /* by the hardware, use nearest possible rate.         */ 
    exact_rate = rate;
    if (snd_pcm_hw_params_set_rate_near(pcm_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.n 
                       ==> Using %d Hz instead.n", rate, exact_rate);
    }

    /* Set number of channels */
    if (snd_pcm_hw_params_set_channels(pcm_handle, 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(pcm_handle, hwparams, periods, 0) < 0) {
      fprintf(stderr, "Error setting periods.n");
      return(-1);
    }

缓冲区尺寸的单元依赖于函数.一些时候是字节,一些时候是必须指定的帧的数目.一个帧是对所有声道的采样数据数组.对于16位立体声数据,一个帧的长度是4个字节.

    /* Set buffer size (in frames). The resulting latency is given by */
    /* latency = periodsize * periods / (rate * bytes_per_frame)     */
    if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods)>>2) < 0) {
      fprintf(stderr, "Error setting buffersize.n");
      return(-1);
    }
  
如果你的硬件不支持2的N次方的缓冲区大小,你可以使用snd_pcm_hw_params_set_buffer_size_near函数.这个函数工作起来与snd_pcm_hw_params_set_rate_near相似.现在我们为PCM设备申请由pcm_handle指向的设置选项.这同样也将准备好PCM设备.
  
    /* Apply HW parameter settings to */
    /* PCM device and prepare device  */
    if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
      fprintf(stderr, "Error setting HW params.n");
      return(-1);
    }
  
在PCM设备被设置以后,我们可以开始对其写PCM数据.第一个写访问将开始PCM回放.对于交错写访问,我们使用函数:
   
    /* Write num_frames frames from buffer data to    */ 
    /* the PCM device pointed to by pcm_handle.       */
    /* Returns the number of frames actually written. */
    snd_pcm_sframes_t snd_pcm_writei(pcm_handle, data, num_frames);
  
对于非交错访问,我们将必须使用函数:

    /* Write num_frames frames from buffer data to    */ 
    /* the PCM device pointed to by pcm_handle.       */ 
    /* Returns the number of frames actually written. */
    snd_pcm_sframes_t snd_pcm_writen(pcm_handle, data, num_frames);
  
在PCM回放开始之后,我们必须确保我们的应用程序发送足够的数据到声卡缓冲区.否则,将发生缓冲区欠载.当这样一个缓冲区欠载发生以后,snd_pcm_prepare将被调用.一个简单的立体声锯齿波能以这样的方式生成:

    unsigned char *data;
    int pcmreturn, l1, l2;
    short s1, s2;
    int frames;

    data = (unsigned char *)malloc(periodsize);
    frames = periodsize >> 2;
    for(l1 = 0; l1 < 100; l1++) {
      for(l2 = 0; l2 < num_frames; l2++) {
        s1 = (l2 % 128) * 100 - 5000;  
        s2 = (l2 % 256) * 100 - 5000;  
        data[4*l2] = (unsigned char)s1;
        data[4*l2+1] = s1 >> 8;
        data[4*l2+2] = (unsigned char)s2;
        data[4*l2+3] = s2 >> 8;
      }
      while ((pcmreturn = snd_pcm_writei(pcm_handle, data, frames)) < 0) {
        snd_pcm_prepare(pcm_handle);
       fprintf(stderr,"<<<<<<<<<<<<<<< BufferUnderrun>>>>>>>>>>>>>>>n");
      }
    }

如果我们想中止回放,我们既可以使用snd_pcm_drop,也可以使用snd_pcm_drain.第一个函数将立即中止回放并丢弃未回放的帧.后一个函数将在回放完所有帧后中止回放.

    /* Stop PCM device and drop pending frames */
    snd_pcm_drop(pcm_handle);

    /* Stop PCM device after pending frames have been played */ 
    snd_pcm_drain(pcm_handle);

(全文完)

参考链接:

原文
http://www.suse.de/~mana/alsa090_howto.html

Linux音频编程指南
http://www-128.ibm.com/developerworks/cn/linux/l-audio/

A Tutorial on Using the ALSA Audio API
http://www.equalarea.com/paul/alsa-audio.html

The ALSA library API reference
http://www.alsa-project.org/alsa-doc/alsa-lib/
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

近日,美国研究机构TechInsights经过拆解与分析认为,麒麟9010是中芯国际制造的,所用工艺是第二代7nm,也就是N+2。

关键字: 华为 芯片 处理器

4月28日,开放式耳机品牌鲸语系列新品于“2024年第五届中关村硬核科技嘉年华”正式亮相。此次发布的新品中,拥有星空黑与暮沙白双配色的防水骨传导运动耳机——鲸语Alpha,以其独特的设计理念和水陆两栖的卓越性能,吸引了众...

关键字: 开放式耳机 运动耳机 鲸语Alpha

作为一名数码爱好者,更是资深理工男,自然也对3D打印设备兴趣满满,正好看到全球消费级光固化3D打印第一品牌的ELEGOO爱乐酷推出了一款重磅新品,性能上也有非常多颠覆升级,便迫不及待入手一台感受一下。

关键字: 打印机

京元电子在重大讯息说明会中宣布,将出售持有苏州子公司京隆科技 92.1619% 的股权,预估交易金额约 48.85 亿人民币,将于第三季度完成交易,届时将退出中国大陆半导体制造业务。

关键字: 半导体制造 半导体封测 封装测试 京元电子

电子数据的存储与共享在我们生活中占据越来越重要的地位,而传统的硬盘存储已然难以满足人们日益增长的数据存储需求,为此网络附加存储(NAS)则以其便捷、高效的特点,逐渐受到广大用户的青睐。但是提到NAS,很多人可能会觉得它是...

关键字: 存储 铁威马NAS 硬盘存储

4月25日,以“分享鸿蒙技术特性,交流鸿蒙生态共建”为主题的HDD·行业沙龙在江西武功山成功举行。华为产品专家们现场带来了诸多精彩分享,吸引了来自政务、金融、新闻资讯等多个行业的四十余家软件服务商到场参加。

关键字: 鸿蒙 华为 智能设备

4月25日,2024(第十八届)北京国际汽车展览会拉开序幕,车展以“新时代·新汽车”为主题,一直持续到5月4日。本次车展将有全球首发车117台(其中跨国公司全球首发车30台),41款概念车及278款新能源车型展出。

关键字: 北京车展 新能源汽车 电动汽车

LED驱动模块RSC6218A 5W-18W迷你高效驱动电源应用,小功率、小体积、高效率

关键字: LED驱动模块 驱动电源应用 LED电源芯片

业内消息,近日台积电在北美技术研讨会上宣布,正在研发 CoWoS 封装技术的下个版本,可以让系统级封装(SiP)尺寸增大两倍以上,实现 120x120mm 的超大封装,功耗可以达到千瓦级别。

关键字: CoWoS 台积电 封装

据外媒报道,字节正在内部探索出售TikTok美国业务多数股权,并援引内部人士披露的信息称 “沃尔玛或为最理想买家”。报道还称,讨论中的一种情况是字节出售美国50%以上TikTok股份,但保留少数股权。

关键字: 字节跳动 TikTok
关闭
关闭