一、数字音频

音频信号是一种连续变化的模拟信号,但计算机仅仅能处理和记录二进制的数字信号。由自然音源得到的音频信号必须经过一定的变换,成为数字音频信号之后,才干送到计算机中作进一步的处理。

数字音频系统通过将声波的波型转换成一系列二进制数据,来实现对原始声音的重现,实现这一步骤的设备常被称为模/数转换器(A/D)。A/D转换器以每秒钟上万次的速率对声波进行採样,每个採样点都记录下了原始模拟声波在某一时刻的状态,通常称之为样本(sample),而每一秒钟所採样的数目则称为採样频率,通过将一串连续的样本连接起来。就能够在计算机中描写叙述一段声音了。对于採样过程中的每个样本来说,数字音频系统会分配一定存储位来记录声波的振幅,一般称之为採样分辨率或者採样精度,採样精度越高,声音还原时就会越细腻。

数字音频涉及到的概念许多,对于在Linux下进行音频编程的程序猿来说。最重要的是理解声音数字化的两个关键步骤:採样和量化

採样就是每隔一定时间就读一次声音信号的幅度。而量化则是将採样得到的声音信号幅度转换为数字值,从本质上讲,採样是时间上的数字化,而量化则是幅度上的数字化。

以下介绍几个在进行音频编程时常常须要用到的技术指标:

採样频率

採样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。採样频率的选择应该遵循奈奎斯特(Harry Nyquist)採样理论:假设对某一模拟信号进行採样,则採样后可还原的最高信号频率仅仅有採样频率的一半,或者说仅仅要採样频率高于输入信号最高频率的两倍,就能从採样信号系列重构原始信号。

正常人听觉的频率范围大约在20Hz~20kHz之间,依据奈奎斯特採样理论,为了保证声音不失真,採样频率应该在40kHz左右。经常使用的音频採样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,假设採用更高的採样频率,还能够达到DVD的音质。

量化位数 

量化位数是对模拟音频信号的幅度进行数字化。它决定了模拟信号数字化以后的动态范围,经常使用的有8位、12位和16位。

量化位越高,信号的动态范围越大,数字化后的音频信号就越可能接近原始信号,但所须要的存贮空间也越大。

声道数 

声道数是反映音频数字化质量的还有一个重要因素,它有单声道和双声道之分。

双声道又称为立体声,在硬件中有两条线路,音质和音色都要优于单声道,但数字化后占领的存储空间的大小要比单声道多一倍。

二、声卡驱动

出于对安全性方面的考虑,Linux下的应用程序无法直接对声卡这类硬件设备进行操作,而是必须通过内核提供的驱动程序才干完毕。在Linux上进行音频编程的本质就是要借助于驱动程序,来完毕对声卡的各种操作。对硬件的控制涉及到寄存器中各个比特位的操作,通常这是与设备直接相关而且对时序的要求很严格,假设这些工作都交由应用程序猿来负责。那么对声卡的编程将变得异常复杂而困难起来,驱动程序的作用正是要屏蔽硬件的这些底层细节,从而简化应用程序的编写。

眼下Linux下经常使用的声卡驱动程序主要有两种:OSSALSA

ALSA和OSS最大的不同之处在于ALSA是由志愿者维护的自由项目,而OSS则是由公司提供的商业产品,因此在对硬件的适应程度上OSS要优于ALSA,它可以支持的声卡种类很多其它。

ALSA尽管不及OSS运用得广泛。但却具有更加友好的编程接口,而且全然兼容于OSS,相应用程序猿来讲无疑是一个更佳的选择。

三、Linux  OSS音频设备驱动

3.1 OSS驱动的组成

OSS标准中有2个最主要的音频设备:mixer(混音器)和DSP(数字信号处理器)。

在声卡的硬件电路中,mixer是一个非常重要的组成部分,它的作用是将多个信号组合或者叠加在一起,对于不同的声卡来说,其混音器的作用可能各不同样。

OSS驱动中。/dev/mixer设备文件是应用程序对mixer进行操作的软件接口。

混音器电路通常由两个部分组成:输入混音器(input mixer)和输出混音器(output mixer)。

输入混音器负责从多个不同的信号源接收模拟信号,这些信号源有时也被称为混音通道或者混音设备。模拟信号通过增益控制器和由软件控制的音量调节器后,在不同的混音通道中进行级别(level)调制。然后被送到输入混音器中进行声音的合成。混音器上的电子开关能够控制哪些通道中有信号与混音器相连。有些声卡仅仅同意连接一个混音通道作为录音的音源,而有些声卡则同意对混音通道做随意的连接。经过输入混音器处理后的信号仍然为模拟信号。它们将被送到A/D转换器进行数字化处理。

输出混音器的工作原理与输入混音器类似,相同也有多个信号源与混音器相连。并且事先都经过了增益调节。当输出混音器对全部的模拟信号进行了混合之后,通常还会有一个总控增益调节器来控制输出声音的大小,此外另一些音调控制器来调节输出声音的音调。经过输出混音器处理后的信号也是模拟信号,它们终于会被送给喇叭或者其他的模拟输出设备。

对混音器的编程包含如何设置增益控制器的级别。以及如何在不同的音源间进行切换。这些操作通常来讲是不连续的,并且不会像录音或者放音那样须要占用大量的计算机资源。

因为混音器的操作不符合典型的读/写操作模式,因此除了
open()和close()两个系统调用之外,大部分的操作都是通过ioctl()系统调用来完毕的。与/dev/dsp不同,/dev/mixer同意多个应用程序同一时候訪问。而且混音器的设置值会一直保持到相应的设备文件被关闭为止。

DSP也称为编解码器,实现录音(录音)和放音(播放)。其相应的设备文件是/dev/dsp或/dev/sound/dsp。OSS声卡驱动程序提供的 /dev/dsp是用于数字採样和数字录音的设备文件。向该设备写数据即意味着激活声卡上的D/A转换器进行放音,而向该设备读数据则意味着激活声卡上的
A/D转换器进行录音。

在从DSP设备读取数据时,从声卡输入的模拟信号经过A/D转换器变成数字採样后的样本,保存在声卡驱动程序的内核缓冲区中,当应用程序通过 read()系统调用从声卡读取数据时,保存在内核缓冲区中的数字採样结果将被拷贝到应用程序所指定的用户缓冲区中。须要指出的是。声卡採样频率是由内核中的驱动程序所决定的,而不取决于应用程序从声卡读取数据的速度。假设应用程序读取数据的速度过慢,以致低于声卡的採样频率,那么多余的数据将会被丢弃(即overflow)。假设读取数据的速度过快,以致高于声卡的採样频率。那么声卡驱动程序将会堵塞那些请求数据的应用程序。直到新的数据到来为止。

在向DSP设备写入数据时。数字信号会经过D/A转换器变成模拟信号。然后产生出声音。应用程序写入数据的速度应该至少等于声卡的採样频率,过慢会产生声音暂停或者停顿的现象(即underflow)。假设用户写入过快的话,它会被内核中的声卡驱动程序堵塞,直到硬件有能力处理新的数据为止。

与其他设备有所不同,声卡通常不须要支持非堵塞(non-blocking)的I/O操作。

即便内核OSS驱动提供了非堵塞的I/O支持,用户空间也不宜採用。

不管是从声卡读取数据,或是向声卡写入数据,其实都具有特定的格式(format)。如无符号8位、单声道、8KHz採样率,假设默认值无法达到要求,能够通过ioctl()系统调用来改变它们。通常说来,在应用程序中打开设备文件/dev/dsp之后。接下去就应该为其设置恰当的格式。然后才干从声卡读取或者写入数据。

3.2 mixer接口

int register_sound_mixer(structfile_operations *fops, int dev);

上述函数用于注冊1个混音器,第1个參数fops即是文件操作接口,第2个參数dev是设备编号,假设填入-1,则系统自己主动分配1个设备编号。

mixer 是 1个典型的字符设备,因此编码的主要工作是实现file_operations中的open()、ioctl()等函数。

mixer接口file_operations中的最重要函数是ioctl()。它实现混音器的不同IO控制命令。

3.3 DSP接口

int register_sound_dsp(structfile_operations *fops, int dev);

上述函数与register_sound_mixer()类似,它用于注冊1个dsp设备,第1个參数fops即是文件操作接口,第2个參数dev是设备编号,假设填入-1。则系统自己主动分配1个设备编号。

dsp也是1个典型的字符设备。因此编码的主要工作是实现file_operations中的read()、write()、ioctl()等函数。

dsp接口file_operations中的read()和write()函数很重要。read()函数从音频控制器中获取录音数据到缓冲区并复制到用户空间,write()函数从用户空间拷贝音频数据到内核空间缓冲区并终于发送到音频控制器。

dsp接口file_operations中的ioctl()函数处理对採样率、量化精度、DMA缓冲区块大小等參数设置IO控制命令的处理。

在数据从缓冲区复制到音频控制器的过程中,一般会使用DMA,DMA对声卡而言很重要。比如,在放音时。驱动设置完DMA控制器的源数据地址(内存中 DMA缓冲区)、目的地址(音频控制器FIFO)和DMA的数据长度,DMA控制器会自己主动发送缓冲区的数据填充FIFO。直到发送完对应的数据长度后才中断一次。

在OSS驱动中。建立存放音频数据的环形缓冲区(ring buffer)一般是值得推荐的方法。

此外,在OSS驱动中,通常会将1个较大的DMA缓冲区分成若干个大小同样的块(这些块也被称为“段”,即
fragment
),驱动程序使用DMA每次在声音缓冲区和声卡之间搬移一个fragment。在用户空间。能够使用ioctl()系统调用来调整块的大小和个数。

除了read()、write()和ioctl()外,dsp接口的poll()函数通常也须要被实现,以向用户反馈眼下是否能读写DMA缓冲区。

在OSS驱动初始化过程中,会调用register_sound_dsp()和register_sound_mixer()注冊dsp和mixer设备。在模块卸载的时候。会调用unregister_sound_dsp(audio_dev_dsp)和unregister_sound_mixer(audio_dev_mixer)。

Linux OSS驱动结构例如以下图所看到的:

3.4 OSS用户空间编程

1、DSP编程

DSP接口的操作一般包含例如以下几个步骤:


打开设备文件/dev/dsp

採用何种模式对声卡进行操作也必须在打开设备时指定,对于不支持全双工的声卡来说,应该使用仅仅读或者仅仅写的方式打开,仅仅有那些支持全双工的声卡,才干以读写的方式打开,这还依赖于驱动程序的详细实现。Linux同意应用程序多次打开或者关闭与声卡相应的设备文件,从而可以非常方便地在放音状态和录音状态之间进行切换。


假设有须要,设置缓冲区大小

执行在Linux内核中的声卡驱动程序专门维护了一个缓冲区,其大小会影响到放音和录音时的效果,使用ioctl()系统调用能够对它的尺寸进行恰当的设置。调节驱动程序中缓冲区大小的操作不是必须的,假设没有特殊的要求,一般採用默认的缓冲区大小也就能够了。

假设想设置缓冲区的大小。则通常应紧跟在设备文件打开之后,这是由于对声卡的其他操作有可能会导致驱动程序无法再改动其缓冲区的大小。


设置声道(channel)数量

依据硬件设备和驱动程序的详细情况。能够设置为单声道或者立体声。


设置採样格式和採样频率

採样格式包含AFMT_U8(无符号8位)、AFMT_S8(有符号8位)、AFMT_U16_LE(小端模式,无符号16位)、 AFMT_U16_BE(大端模式,无符号16位)、AFMT_MPEG、AFMT_AC3等。使用SNDCTL_DSP_SETFMT IO控制命令能够设置採样格式。

对于大多数声卡来说。其支持的採样频率范围一般为5kHz到44.1kHz或者48kHz。但并不意味着该范围内的全部连续频率都会被硬件支持,在 Linux下进行音频编程时最经常使用到的几种採样频率是11025Hz、16000Hz、22050Hz、32000Hz 和44100Hz。使用SNDCTL_DSP_SPEED IO控制命令能够设置採样频率。


读写/dev/dsp实现播放或录音

2. mixer编程

声卡上的混音器由多个混音通道组成,它们能够通过驱动程序提供的设备文件/dev/mixer进行编程。

对声卡的输入增益和输出增益进行调节是混音器的一个主要作用,眼下大部分声卡採用的是8位或者16位的增益控制器,声卡驱动程序会将它们转换成百分比的形式。也就是说不管是输入增益还是输出增益。其取值范围都是从0~100。

Linux音频驱动简述的更多相关文章

  1. Linux音频驱动-ALSA概述

    概述 ALSA(Advanced Linux Sound Architecture)是linux上主流的音频结构,在没有出现ALSA架构之前,一直使用的是OSS(Open Sound System)音 ...

  2. Linux音频驱动学习之:(1)ASOC分析

    一.音频架构概述 (1)ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和 ...

  3. 小白自制Linux开发板 八. Linux音频驱动配置

    不知不觉小白自制开发板系列已经到第八篇了,本篇要配置的是音频驱动,也算是硬件部分的最后一片了,积攒的文章也差不多全放完了,后续更新可能会放缓,还请见谅. 对于F1C200s是自带了多媒体处理功能的,所 ...

  4. Linux音频驱动学习之:(2)移植wm8976声卡驱动(linux-3.4.2)

    1.wm8976驱动程序: /* * wm8976.h -- WM8976 Soc Audio driver * * This program is free software; you can re ...

  5. linux 音频驱动

    转:https://wenku.baidu.com/view/7394e16d7e21af45b307a8dc.html?pn=51 linux_sound_alsa_ALSA体系SOC子系统中数据流 ...

  6. linux音频alsa-uda134x驱动文档阅读之一转自http://blog.csdn.net/wantianpei/article/details/7817293

    前言 目前,linux系统常用的音频驱动有两种形式:alsa oss alsa:现在是linux下音频驱动的主要形式,与简单的oss兼容.oss:过去的形式而我们板子上的uda1341用的就是alsa ...

  7. 嵌入式驱动开发之---Linux ALSA音频驱动(一)

    本文的部分内容参考来自DroidPhone的博客(http://blog.csdn.net/droidphone/article/details/6271122),关于ALSA写得很不错的文章,只是少 ...

  8. 基于Linux ALSA音频驱动的wav文件解析及播放程序 2012

    本设计思路:先打开一个普通wav音频文件,从定义的文件头前面的44个字节中,取出文件头的定义消息,置于一个文件头的结构体中.然后打开alsa音频驱动,从文件头结构体取出采样精度,声道数,采样频率三个重 ...

  9. Linux MMC 驱动子系统简述(源码剖析)

    1. Linux MMC 驱动子系统 块设备是Linux系统中的基础外设之一,而 MMC/SD 存储设备是一种典型的块设备.Linux内核设计了 MMC子系统,用于管理 MMC/SD 设备. MMC ...

随机推荐

  1. JavaScript中创建对象的5种模式

    构造函数模式 实现方式: function Person(name, age, job) { this.name = name; this.age = age; this.job = job; thi ...

  2. java - 线程1打印1-10,当线程打印到5后,线程2打印“hello”,然后线程1继续打印

    public class T { private static int a =1;//1代表线程1 2线程2 public static void main(String[] args) { fina ...

  3. hihocoder 1145 : 幻想乡的日常

    #1145 : 幻想乡的日常 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 幻想乡一共有n处居所,编号从1到n.这些居所被n-1条边连起来,形成了一个树形的结构. 每处 ...

  4. POJ-2398

    Toy Storage Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4243   Accepted: 2517 Descr ...

  5. log4j日志相对路径,Tomcat(第三方和Springboot内置)参数catalina.home和catalina.base的设置

    关于Log4j日志相对路径的配置请看:log4j 产生的日志位置设置 和 catalina.home.catalina.base . 由于我们在Log4j的配置中引入了系统属性${catalina.b ...

  6. JAVA中静态块、静态变量加载顺序详解

    http://blog.csdn.net/mrzhoug/article/details/51581994 一般顺序:静态块(静态变量)——>成员变量——>构造方法——>静态方法

  7. redis 安装配置

    reids 安装配置 1.1 下载软件包 [root@node01 ~]# mkdir -p /data/src/ [root@node01 ~]# cd /data/src/ [root@node0 ...

  8. 微信小程序-如何自定义导航栏(navigationStyle)?

    小程序是越来越开放了,微信版本 6.6.0可以自定义导航? 先了解下app.json中window配置navigationStyle属性,即导航栏样式,仅支持 default/custom.custo ...

  9. 使用PuTTY连接树莓派

    这是 meelo 原创的 玩转树莓派 系列文章 PuTTY是一个支持Telnet.SSH协议,实现远程登录的软件.树莓派的官方操作系统Raspbian默认开启了SSH协议进行登录,这样即使没有专门的显 ...

  10. 【剑指offer】面试题 10. 斐波那契数列

    面试题 10. 斐波那契数列 题目一:求斐波那契数列的第n项 题目描述:求斐波拉契数列的第n项 写出一个函数,输入n,求斐波拉契(Fibonacci)数列的第n项.斐波拉契数列定义如下: C++ 实现 ...