翻译文章的链接:

http://equalarea.com/paul/alsa-audio.html

关于怎么使用ALSA API教程

这份文档帮助对ALSA API使用入门。不是一个完整的ALSA API参考手册(详细参考手册地址是:http://www.alsa-project.org/alsa-doc/alsa-lib/),并且不包括复杂软件需要处理的细节部分。而它为有编程经验的但对这些API是新手的程序员提供充足的背景知识,以写出一个简单的程序。在这份文档中所有的代码的许可证遵守GNU Public License的,如果你打算在其他许可证下用ALSA写软件,我建议你去发现其他的文档。  主要内容:

(1)理解音频接口

(2)典型音频应用是什么样子的

(3)一个最小回放程序

(4)一个最小采集程序

(5)一个最小基于中断驱动的程序

(6)写程序过程

打开设备

设置参数

接受和发送数据

理解音频接口

我们首先回顾音频接口的基本设计。作为一个应用程序程序员,实际你没有必要为操作层担忧,它由ALSA提供的一个插件设备驱动全部考录,但是如果你想要些一个有效率并且灵活的软件,你必须在概念上理解操作层都做了些什么。

(音频接口、音频设备、声卡。在此处是一个概念。)

音频接口是一个设备(译者注:声卡),允许计算机从外界接受和向外界发送音频数据。在计算机内部,音频数据像其他任何数据一样代表了比特流。然而,声音接口可以发送数字声音信号和接受模拟声音信号。这两种情况下,计算机使用的代表声音的比特流需要被转化,在被送到外界之前,同样,被音频接口接受的外部信号也需要被转化,使之变为对计算机有用的数据(可用的数据)。这两个转化就是音频接口存在的目的

在音频接口中,有个区域被称为“硬件缓冲区”,当一个外界音频信号到达后,这个设备转换这个模拟信号为计算机可以使用的比特流,并且存储这些数据在硬件缓冲区,被用来传递给计算机。当在硬件缓冲区采集到了足够多的数据,音频接口对计算机产生一个中断,以告诉计算机数据准备好了,可以从硬件缓冲区,取出了。相反,对于数据从计算机内部送到外界时,有一个和以上有个相似的过程。设备产生对计算机产生中断,告诉计算机,硬件缓冲区有空间,计算机可以存数据在硬件缓冲区。设备然后转化这些比特流为外界需要的无论什么格式,并且发送它。理解设备使用这个缓冲区作为一个循环缓冲区是很重要的。当到达了缓冲区的末尾,指针回到头,继续开始。

为了这个过程可以正确的运行,一些变量需要被配置,他们包括:

(1)当在计算机使用的比特流和外界使用的信号之间的转换时,设备应该使用什么格式?

(2)采样样本以多少的比特率在设备和计算机直接传递?

(3)设备对计算机产生中断时,硬件缓冲区应该有多少数据(接受)或者空间(发送)?

(4)硬件缓冲区应该多大?

头两个问题是音频数据质量调节的基础。后两个问题是影响音频信号的延时,这个延时指的是在这些之间的延时:

1.输入延时:由设备从外界采集到的音频数据到达计算机之间。

2.输出延时:由计算机发送的数据,到达外界时之间的延时。

这两者对于很多的音频软件是很重要的,尽管一些程序不需要关心这些事情。

最小的播放程序

这个程序用播放模式打开一个音频设备,配置为立体音,16bit(位深),采样频率44.1KHZ,交错模式,常见的read/write存取模式。然后,发送一块任意数据到设备中,最后退出程序。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <alsa/asoundlib.h>
  4.  
  5. main (int argc, char *argv[])
  6. {
  7. int i;
  8. int err;
  9. short buf[];
  10. snd_pcm_t *playback_handle;
  11. snd_pcm_hw_params_t *hw_params;
  12.  
  13. if ((err = snd_pcm_open (&playback_handle, argv[], SND_PCM_STREAM_PLAYBACK, )) < ) {
  14. fprintf (stderr, "cannot open audio device %s (%s)\n",
  15. argv[],
  16. snd_strerror (err));
  17. exit ();
  18. }
  19.  
  20. if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
  21. fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
  22. snd_strerror (err));
  23. exit ();
  24. }
  25.  
  26. if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < ) {
  27. fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
  28. snd_strerror (err));
  29. exit ();
  30. }
  31.  
  32. if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
  33. fprintf (stderr, "cannot set access type (%s)\n",
  34. snd_strerror (err));
  35. exit ();
  36. }
  37.  
  38. if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
  39. fprintf (stderr, "cannot set sample format (%s)\n",
  40. snd_strerror (err));
  41. exit ();
  42. }
  43.  
  44. if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, , )) < ) {
  45. fprintf (stderr, "cannot set sample rate (%s)\n",
  46. snd_strerror (err));
  47. exit ();
  48. }
  49.  
  50. if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, )) < ) {
  51. fprintf (stderr, "cannot set channel count (%s)\n",
  52. snd_strerror (err));
  53. exit ();
  54. }
  55.  
  56. if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < ) {
  57. fprintf (stderr, "cannot set parameters (%s)\n",
  58. snd_strerror (err));
  59. exit ();
  60. }
  61.  
  62. snd_pcm_hw_params_free (hw_params);
  63.  
  64. if ((err = snd_pcm_prepare (playback_handle)) < ) {
  65. fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
  66. snd_strerror (err));
  67. exit ();
  68. }
  69.  
  70. for (i = ; i < ; ++i) {
  71. if ((err = snd_pcm_writei (playback_handle, buf, )) != ) {
  72. fprintf (stderr, "write to audio interface failed (%s)\n",
  73. snd_strerror (err));
  74. exit ();
  75. }
  76. }
  77.  
  78. snd_pcm_close (playback_handle);
  79. exit ();
  80. }

最小采集程序:

为了采集音频数据,程序打开一个音频设备,配置为立体音,位深16bit,采集频率为44.1KHz,交错模式,常见的read/write存取数据,然后读取一块任意数据从硬件缓冲区,最后退出程序。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <alsa/asoundlib.h>
  4.  
  5. main (int argc, char *argv[])
  6. {
  7. int i;
  8. int err;
  9. short buf[];
  10. snd_pcm_t *capture_handle;
  11. snd_pcm_hw_params_t *hw_params;
  12.  
  13. if ((err = snd_pcm_open (&capture_handle, argv[], SND_PCM_STREAM_CAPTURE, )) < ) {
  14. fprintf (stderr, "cannot open audio device %s (%s)\n",
  15. argv[],
  16. snd_strerror (err));
  17. exit ();
  18. }
  19.  
  20. if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
  21. fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
  22. snd_strerror (err));
  23. exit ();
  24. }
  25.  
  26. if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < ) {
  27. fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
  28. snd_strerror (err));
  29. exit ();
  30. }
  31.  
  32. if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
  33. fprintf (stderr, "cannot set access type (%s)\n",
  34. snd_strerror (err));
  35. exit ();
  36. }
  37.  
  38. if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
  39. fprintf (stderr, "cannot set sample format (%s)\n",
  40. snd_strerror (err));
  41. exit ();
  42. }
  43.  
  44. if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, , )) < ) {
  45. fprintf (stderr, "cannot set sample rate (%s)\n",
  46. snd_strerror (err));
  47. exit ();
  48. }
  49.  
  50. if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, )) < ) {
  51. fprintf (stderr, "cannot set channel count (%s)\n",
  52. snd_strerror (err));
  53. exit ();
  54. }
  55.  
  56. if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < ) {
  57. fprintf (stderr, "cannot set parameters (%s)\n",
  58. snd_strerror (err));
  59. exit ();
  60. }
  61.  
  62. snd_pcm_hw_params_free (hw_params);
  63.  
  64. if ((err = snd_pcm_prepare (capture_handle)) < ) {
  65. fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
  66. snd_strerror (err));
  67. exit ();
  68. }
  69.  
  70. for (i = ; i < ; ++i) {
  71. if ((err = snd_pcm_readi (capture_handle, buf, )) != ) {
  72. fprintf (stderr, "read from audio interface failed (%s)\n",
  73. snd_strerror (err));
  74. exit ();
  75. }
  76. }
  77.  
  78. snd_pcm_close (capture_handle);
  79. exit ();
  80. }

程序用播放模式,打开一个音频设备,配置为立体音,位深16bit,采集频率为44.1KHz,交错模式,常见的read/write存取数据。程序一直等待,直到设备准备好了接受回放数据,准备好同时,发送任意数据到硬件缓冲区,这个设计允许你的程序被移植到依靠回调函数驱动的机制,例如,jack,ladspa,coreaudio,vst 和其他。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <errno.h>
  4. #include <poll.h>
  5. #include <alsa/asoundlib.h>
  6.  
  7. snd_pcm_t *playback_handle;
  8. short buf[];
  9.  
  10. int
  11. playback_callback (snd_pcm_sframes_t nframes)
  12. {
  13. int err;
  14.  
  15. printf ("playback callback called with %u frames\n", nframes);
  16.  
  17. /* ... fill buf with data ... */
  18.  
  19. if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < ) {
  20. fprintf (stderr, "write failed (%s)\n", snd_strerror (err));
  21. }
  22.  
  23. return err;
  24. }
  25.  
  26. main (int argc, char *argv[])
  27. {
  28.  
  29. snd_pcm_hw_params_t *hw_params;
  30. snd_pcm_sw_params_t *sw_params;
  31. snd_pcm_sframes_t frames_to_deliver;
  32. int nfds;
  33. int err;
  34. struct pollfd *pfds;
  35.  
  36. if ((err = snd_pcm_open (&playback_handle, argv[], SND_PCM_STREAM_PLAYBACK, )) < ) {
  37. fprintf (stderr, "cannot open audio device %s (%s)\n",
  38. argv[],
  39. snd_strerror (err));
  40. exit ();
  41. }
  42.  
  43. if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
  44. fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
  45. snd_strerror (err));
  46. exit ();
  47. }
  48.  
  49. if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < ) {
  50. fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
  51. snd_strerror (err));
  52. exit ();
  53. }
  54.  
  55. if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
  56. fprintf (stderr, "cannot set access type (%s)\n",
  57. snd_strerror (err));
  58. exit ();
  59. }
  60.  
  61. if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
  62. fprintf (stderr, "cannot set sample format (%s)\n",
  63. snd_strerror (err));
  64. exit ();
  65. }
  66.  
  67. if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, , )) < ) {
  68. fprintf (stderr, "cannot set sample rate (%s)\n",
  69. snd_strerror (err));
  70. exit ();
  71. }
  72.  
  73. if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, )) < ) {
  74. fprintf (stderr, "cannot set channel count (%s)\n",
  75. snd_strerror (err));
  76. exit ();
  77. }
  78.  
  79. if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < ) {
  80. fprintf (stderr, "cannot set parameters (%s)\n",
  81. snd_strerror (err));
  82. exit ();
  83. }
  84.  
  85. snd_pcm_hw_params_free (hw_params);
  86.  
  87. /* tell ALSA to wake us up whenever 4096 or more frames
  88. of playback data can be delivered. Also, tell
  89. ALSA that we'll start the device ourselves.
  90. */
  91.  
  92. if ((err = snd_pcm_sw_params_malloc (&sw_params)) < ) {
  93. fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
  94. snd_strerror (err));
  95. exit ();
  96. }
  97. if ((err = snd_pcm_sw_params_current (playback_handle, sw_params)) < ) {
  98. fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
  99. snd_strerror (err));
  100. exit ();
  101. }
  102. if ((err = snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, )) < ) {
  103. fprintf (stderr, "cannot set minimum available count (%s)\n",
  104. snd_strerror (err));
  105. exit ();
  106. }
  107. if ((err = snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)) < ) {
  108. fprintf (stderr, "cannot set start mode (%s)\n",
  109. snd_strerror (err));
  110. exit ();
  111. }
  112. if ((err = snd_pcm_sw_params (playback_handle, sw_params)) < ) {
  113. fprintf (stderr, "cannot set software parameters (%s)\n",
  114. snd_strerror (err));
  115. exit ();
  116. }
  117.  
  118. /* the interface will interrupt the kernel every 4096 frames, and ALSA
  119. will wake up this program very soon after that.
  120. */
  121.  
  122. if ((err = snd_pcm_prepare (playback_handle)) < ) {
  123. fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
  124. snd_strerror (err));
  125. exit ();
  126. }
  127.  
  128. while () {
  129.  
  130. /* wait till the interface is ready for data, or 1 second
  131. has elapsed.
  132. */
  133.  
  134. if ((err = snd_pcm_wait (playback_handle, )) < ) {
  135. fprintf (stderr, "poll failed (%s)\n", strerror (errno));
  136. break;
  137. }
  138.  
  139. /* find out how much space is available for playback data */
  140.  
  141. if ((frames_to_deliver = snd_pcm_avail_update (playback_handle)) < ) {
  142. if (frames_to_deliver == -EPIPE) {
  143. fprintf (stderr, "an xrun occured\n");
  144. break;
  145. } else {
  146. fprintf (stderr, "unknown ALSA avail update return value (%d)\n",
  147. frames_to_deliver);
  148. break;
  149. }
  150. }
  151.  
  152. frames_to_deliver = frames_to_deliver > ? : frames_to_deliver;
  153.  
  154. /* deliver the data */
  155.  
  156. if (playback_callback (frames_to_deliver) != frames_to_deliver) {
  157. fprintf (stderr, "playback callback failed\n");
  158. break;
  159. }
  160. }
  161.  
  162. snd_pcm_close (playback_handle);
  163. exit ();
  164. }

最小全双工程序:

全双工可以通过结合上述所展示的回放和采集程序实现。虽然,很多的现有的linux音频程序使用这种设计,但是在作者看来,这个是有很大的缺陷。上诉的中断驱动例子在很多场合下代表一个基本的较好的设计。若是将中断驱动扩展为全双工是相当复杂的。这就是为什么我建议你忘记这个。

术语:

捕捉:从外界采集数据(“记录”和采集不同,记录意味着将存储在某地方,不是alsa API的一部分)。

回放:将数据发送到外界,以致外界可以听到声音。

全双工:捕捉和回放在同一个设备同一时间发生的状态。

xrun:一旦音频接口开始运转,它将一直运转,知道告诉它停止。设备将产生数据为计算机去使用或者计算机发送数据到外界。由于很多原因,你的可能跟不上设备的速度。对于回放来说,可能导致一种结果,音频设备需要的数据从计算机中,但是计算机没有及时发送给设备,而导致设备使用留在硬件缓冲区中的旧数据,这种情况叫做underrun。对于捕捉情况,设备产生了计算机可用的数据,但是没有空间去存储这些数据了,设备不得不重写硬件缓冲区的一部分,而被重写的这部分数据计算机还没有得到,这样数据就丢失了,这个叫做overrun。简单起见,我们使用xrun表示这两种情况。

pcm

Pulse Code Modulation(脉冲编码调制),这个术语描述的是一种方法,用来转换模拟信号为数字信号。这种方法被绝大数的计算机音频设备使用,并且ALSA API使用它对音频作为代替。

声道

帧:在单声道中,一个样本是一个单一的值,用来描述在一个采样点上音频信号的振幅。当我们谈论关于数字音频的时候,我们经常谈论这些数据,这些数据描述了在一个采样点上所有的声道。一集合的样本通常叫做帧,每个声道一个集合。我经常用帧数表示时间的流逝,这个大概和人们用样本测量的精度差不多,但是帧更精确。更重要的是,当我们谈论一个采样点上的描述所有声道的数据集合,他是一个单元才有意义。几乎每个ALSA API函数使用帧作为数据数量的测量单元。

交错模式:一种数据布局(格式安排)。在一个采样点上,每个声道上的采样数据连续的紧挨着另一个声道的采样数据。

非交错模式:一种数据布局。一个信道的所有样本数据存储在另外一个信道的数据之后。
采样时钟:一个时钟源,在 计算机采集或播放时标记时间。一些音频设备可以使你使用外部时钟源,不是字时钟就是自动同步时钟。所有的音频设备本省至少一个采样时钟源,典型的是晶体时钟。一些音频设备不允许时钟的频率被改变。一些时钟不是你期望的那样准确的频率运行。没有两个采样时钟被期望运行时有相同的精确的频率。如果你需要两个样本流去保持同步,他们必须使用同一个采样时钟。

怎样使用alsa API的更多相关文章

  1. A Tutorial on Using the ALSA Audio API

    A Tutorial on Using the ALSA Audio API This document attempts to provide an introduction to the ALSA ...

  2. 【转】Alsa音频编程【精华】

    一.前序 这里了解一下各个参数的含义以及一些基本概念. 声音是连续模拟量,计算机将它离散化之后用数字表示,就有了以下几个名词术语. 样本长度(sample):样本是记录音频数据最基本的单位,计算机对每 ...

  3. Alsa 读取wave文件,并播放wave 文件

    对于一个wave文件,如果需要播放,涉及到几个方面 1.对于wave文件的解析 2.通过解析wave文件,将得到的参数(主要是sampfrequency, bitsperSample,channel) ...

  4. 基于Orangpi Zero和Linux ALSA实现WIFI无线音箱(三)

    作品已经完成,先上源码: https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.zip 全文包含三篇,这是第三篇,主要讲述接收端程序的原理和过程. 第一 ...

  5. 【改】利用ALSA库进行音频重采样

    转自:http://www.voidcn.com/article/p-snamarwr-p.html 一.ALSA介绍: 1.简介: 高级Linux声音体系(英语:Advanced LinuxSoun ...

  6. ubuntu alsa

    今天要在linux下搞音频编程,在网上查阅了一下资料,网上很多资料都是在linux下直接对/dev/dsp进行编程的,因为在以往的linux系统中,我们是可以通过cat  xxx.wav /dev/d ...

  7. alsa 编程

    ALSA(Advanced Linux Sound Architecture)是由内核驱动,标准的API库和一系列实用程序组成.因为涉及到版权和BUG的问题Linux 2.6内核抛弃了旧的OSS,AL ...

  8. ALSA lib调用实例

    1. Display Some PCM Types and Formats 2. Opening PCM Device and Setting Parameters /* This example o ...

  9. 经典alsa 录音和播放程序

    这里贴上虚拟机ubuntu下alsa的录音程序(capture.c)和播放程序(playback.c)的源码. 首先要测试一下自己的ubuntu是否打开了声音.这个可以打开/系统/首选项/声音  来调 ...

随机推荐

  1. indexOf()、includes()、startsWith()、endsWith()

    是否包含字符串三种新方法 传统上,JavaScript只有 indexOf 方法,可以用来确定一个字符串是否包含在另一个字符串中.ES6又提供了三种新方法. includes():返回布尔值,表示是否 ...

  2. luogu P3811 【模板】乘法逆元

    题目背景 这是一道模板题 题目描述 给定n,p求1~n中所有整数在模p意义下的乘法逆元. 输入输出格式 输入格式: 一行n,p 输出格式: n行,第i行表示i在模p意义下的逆元. 输入输出样例 输入样 ...

  3. atom 隐藏右边的白线

    atom-text-editor.editor .wrap-guide {//隐藏右边的白线visibility: hidden;}

  4. java正则过虑字符

    public static void main(String[] args) { String testrString = "{\"abc\" : \"[123 ...

  5. n*n的正方形网格中有多少个长方形

    n*n的正方形网格中有横竖各n+1条直线,其中,任意各取两条都可以组成一个长方形﹙正方形也是长方形﹚.所以长方形个数为C﹙n+2,2﹚×C﹙n+2,2﹚=﹙n+1﹚²n²/4个.如果正方形不算,则N= ...

  6. Hibernate注解详解

    一.实体Bean 每个持久化POJO类都是一个实体Bean, 通过在类的定义中使用 @Entity 注解来进行声明. 声明实体Bean @Entitypublic class Flightimplem ...

  7. SharpSSH

    SharpSSH sharpssh is a pure .NET implementation of the SSH2 client protocol suite. It provides an AP ...

  8. 制作一个可以滑动操作的 Table View Cell

    本文转载至 https://github.com/nixzhu/dev-blog Apple 通过 iOS 7 的邮件(Mail)应用介绍了一种新的用户界面方案——向左滑动以显示一个有着多个操作的菜单 ...

  9. Java第三次实验要求

    实验三 类与对象(一) 一. 实验目的 1. 掌握类与对象的基本概念: 2. 掌握类的声明.创建与用法: 3. 掌握类的构造方法的定义与用法 4. 掌握类的成员变量.成员方法的定义与用法: 5. 理解 ...

  10. css3 - target

    通过CSS3伪元素target,我们可以实现拉风琴 源码 <!DOCTYPE HTML> <html lang="en-US"> <head> ...