翻译文章的链接:

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存取模式。然后,发送一块任意数据到设备中,最后退出程序。

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h> main (int argc, char *argv[])
{
int i;
int err;
short buf[];
snd_pcm_t *playback_handle;
snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_open (&playback_handle, argv[], SND_PCM_STREAM_PLAYBACK, )) < ) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[],
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < ) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
fprintf (stderr, "cannot set access type (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, , )) < ) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, )) < ) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < ) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
exit ();
} snd_pcm_hw_params_free (hw_params); if ((err = snd_pcm_prepare (playback_handle)) < ) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit ();
} for (i = ; i < ; ++i) {
if ((err = snd_pcm_writei (playback_handle, buf, )) != ) {
fprintf (stderr, "write to audio interface failed (%s)\n",
snd_strerror (err));
exit ();
}
} snd_pcm_close (playback_handle);
exit ();
}

最小采集程序:

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

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h> main (int argc, char *argv[])
{
int i;
int err;
short buf[];
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_open (&capture_handle, argv[], SND_PCM_STREAM_CAPTURE, )) < ) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[],
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < ) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
fprintf (stderr, "cannot set access type (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, , )) < ) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, )) < ) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < ) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
exit ();
} snd_pcm_hw_params_free (hw_params); if ((err = snd_pcm_prepare (capture_handle)) < ) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit ();
} for (i = ; i < ; ++i) {
if ((err = snd_pcm_readi (capture_handle, buf, )) != ) {
fprintf (stderr, "read from audio interface failed (%s)\n",
snd_strerror (err));
exit ();
}
} snd_pcm_close (capture_handle);
exit ();
}

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

   #include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <poll.h>
#include <alsa/asoundlib.h> snd_pcm_t *playback_handle;
short buf[]; int
playback_callback (snd_pcm_sframes_t nframes)
{
int err; printf ("playback callback called with %u frames\n", nframes); /* ... fill buf with data ... */ if ((err = snd_pcm_writei (playback_handle, buf, nframes)) < ) {
fprintf (stderr, "write failed (%s)\n", snd_strerror (err));
} return err;
} main (int argc, char *argv[])
{ snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
snd_pcm_sframes_t frames_to_deliver;
int nfds;
int err;
struct pollfd *pfds; if ((err = snd_pcm_open (&playback_handle, argv[], SND_PCM_STREAM_PLAYBACK, )) < ) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[],
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_malloc (&hw_params)) < ) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < ) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
fprintf (stderr, "cannot set access type (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < ) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, , )) < ) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, )) < ) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
exit ();
} if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < ) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
exit ();
} snd_pcm_hw_params_free (hw_params); /* tell ALSA to wake us up whenever 4096 or more frames
of playback data can be delivered. Also, tell
ALSA that we'll start the device ourselves.
*/ if ((err = snd_pcm_sw_params_malloc (&sw_params)) < ) {
fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
snd_strerror (err));
exit ();
}
if ((err = snd_pcm_sw_params_current (playback_handle, sw_params)) < ) {
fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
snd_strerror (err));
exit ();
}
if ((err = snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, )) < ) {
fprintf (stderr, "cannot set minimum available count (%s)\n",
snd_strerror (err));
exit ();
}
if ((err = snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)) < ) {
fprintf (stderr, "cannot set start mode (%s)\n",
snd_strerror (err));
exit ();
}
if ((err = snd_pcm_sw_params (playback_handle, sw_params)) < ) {
fprintf (stderr, "cannot set software parameters (%s)\n",
snd_strerror (err));
exit ();
} /* the interface will interrupt the kernel every 4096 frames, and ALSA
will wake up this program very soon after that.
*/ if ((err = snd_pcm_prepare (playback_handle)) < ) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit ();
} while () { /* wait till the interface is ready for data, or 1 second
has elapsed.
*/ if ((err = snd_pcm_wait (playback_handle, )) < ) {
fprintf (stderr, "poll failed (%s)\n", strerror (errno));
break;
} /* find out how much space is available for playback data */ if ((frames_to_deliver = snd_pcm_avail_update (playback_handle)) < ) {
if (frames_to_deliver == -EPIPE) {
fprintf (stderr, "an xrun occured\n");
break;
} else {
fprintf (stderr, "unknown ALSA avail update return value (%d)\n",
frames_to_deliver);
break;
}
} frames_to_deliver = frames_to_deliver > ? : frames_to_deliver; /* deliver the data */ if (playback_callback (frames_to_deliver) != frames_to_deliver) {
fprintf (stderr, "playback callback failed\n");
break;
}
} snd_pcm_close (playback_handle);
exit ();
}

最小全双工程序:

全双工可以通过结合上述所展示的回放和采集程序实现。虽然,很多的现有的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. Java 添加播放MIDI音乐

      Java 在多媒体处理方面的确优势不大,但是我们在程序中有些时候又需要一些音乐. 如果播放的音乐是wav等波形音频文件,又很大的话,所以背景音乐最好就是MIDI了. 网上很多播放MIDI的教程都是 ...

  2. (5)Unity3d GUI

  3. (1)Unity3d界面、入门

    项目视图 层级视图 属性视图 场景视图 游戏视图 调整u3d整体界面布局 1.查看和移动视图 快捷键Q 2.沿轴方向位移 快捷键W 3.沿轴向旋转 快捷键E 4.沿轴向缩放 快捷键R 5.自由调节小大 ...

  4. 初级Springboot(一)

    初级Springboot(一) 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 一.了解Springboot 做Java开发的小伙伴都知道,我们在做项目的时候,需要去写大量的配置文件 ...

  5. TCP server和client的一些测试

    一.TCP server和client测试   socket设置 测试项/测试情景 send recv 测             server block           client bloc ...

  6. Android-->状态栏高度,导航栏高度,Window高度,DecorView高度,heightPixels

    1:DecorView的高度 DecorView的高度代表的是: 整个装饰窗口的高度, 这个高度包括:状态烂的高度和导航栏的高度.(状态栏和导航栏通常叫做装饰窗口, 而ActionBar不属于装饰窗口 ...

  7. JavaScript 推断浏览器类型及32位64位

    JS推断出版本号以及浏览器类型 <script type="text/javascript"> var Sys = {}; var ua = navigator.use ...

  8. window.open 打开子窗体,关闭全部的子窗体

    需求:通过window.open方法打开了子窗体,当关闭主窗体时.子窗体应当也关闭. 实现思路: 1.打开子窗体函数window.open(url,winName)的第二个參数winName能够唯一标 ...

  9. 最长连续序列(Longest Consecutive Sequence)

    Given an unsorted array of integers, find the length of the longest consecutive elements sequence. F ...

  10. 用C++设计一个不能被继承的类(转)

    在Java 中定义了关键字final,被final修饰的类不能被继承. 首先想到的是在C++中,子类的构造函数会自动调用父类的构造函数.同样,子类的析构函数也会自动调用父类的析构函数.要想一个类不能被 ...