目录(?)[-]
- Linux Audio Stack
- oss
- ALSA
- PulseAudio
- ALSA Project
- ALSA体系结构
- 环境搭建
- 测试
- ALSA混音
- ALSA编程
Linux Audio Stack
oss
- OSS借用了UNIX里”一切都是文件”的概念,。
把声卡模拟成一个/dev/dsp设备, 多块声卡就是dsp0, dsp1… - 要播放声音?打开dsp设备.往里面write数据就可以了.
- 设置比特率?用ioctl设置即可.
- 最简单的接口, 也是最没用的接口.因为应用程序完全没法对声音的播放进行控制.
- OSS是个阻塞的接口, write后, 要声音播放完毕才返回.
显然很糟糕, 因为在write和下一次write的微小时间间隔内, 声音出现了不连续.解决办法就是使用异步的write. write完了, 用ioctl轮询, 看还剩下多少数据.注意, 是轮询.轮询到还剩下不到1ms就播放玩了, 马上再write一个buffer过去.这就是OSS接口下的播放.
ALSA
- 当然OSS这个声音接口实在是太简单了, 于是他们搞了一个ALSA接口.ALSA先进了一些, 有了PLL接口.除了可以向声卡发送数据,
-
还可以为数据附带时钟信息.注意, 可以为数据附带时钟信息.意味着你可以不用精确的等待到一个时间再送出数据,
而是指定一块数据在指定的时间播放.还可以设定播放到某个byte的时候, 产生时钟信号通知程序, 好让程序能继续准备下一块buffer继续播放.
-
然后asla又提供了一个高级的接口, libasound.so, libasound.so可以读取
/etc/asound.conf
、/usr/share/alsa/alsa.conf
、~/.asound.conf
配置文件, 还可以加载各种插件, 插件又可以通过配置文件配置.相当的高级.可以说, alsa是为专业的音频应用准备的.
-
不论是OSS还是ALSA, 在民用声卡上都有一个致命的缺点:他们不支持混音.OSS和ALSA都只是声卡的接口.如果声卡本身不支持硬件混音,
那么程序就只能独占声卡.表现为只能有一个程序出声.好在alsa有一个补救措施, 那就是libasound.so.
-
alsa不推荐直接使用alsa的内核接口编程,而是使用libasound.so的接口.如果使用了libasound.so的接口, 用户可以配置一个叫”dmix”的插件.然后把dmix模拟出来的声卡作为默认声卡.大家都输出到dmix, dmix再收集多个程序的声音, 一起混音然后输出给硬件.
-
这个问题看似解决了.实际上没有解决.受限于alsa的接口, dmix的实现质量相当的糟糕.首先, dmix为了混音, 强制重采样.重采样导致了音质的劣化.
其次, dmix没有C/S结构.通过共享内存协作完成混音和共享声卡, 一个程序的dmix出问题, 会导致所有正在发声的程序崩溃.
-
ALSA的另一个缺点, 在使用了多个声卡后(蓝牙和HDMI普及了啊), 也开始暴露.那就是不能动态切换输出声卡.
我希望我带上蓝牙耳机后, 声音能从扬声器直接转移到耳机里播放.但是ALSA的先天缺陷导致无法实现这个功能.
播放器必须重新打开蓝牙声卡.也就是说, 把切换输出的责任全部交给播放器实现.播放器可以实现,
问题是所有的播放器都必须实现.另一个问题是, 用户必须到各个播放器下依次调节.非常麻烦.
PulseAudio
-
为了解决ALSA固有缺陷的目的, PulseAudio又出现了.
PulseAudio解决办法也是提供一个混音服务.混音服务后台执行, 独占声卡的访问权限.其他程序要播放声音的,
都需要将声音数据交给pulseaudio.由pulseaudio执行混音.
-
那么, 重采样问题怎么解决呢?
pulseaudio提供了一个动态采样率模式.简单说明一下工作原理
- A程序播放44100hz的声音, 提交给pulseaudio
- pulseaudio查询到声卡支持44100hz采样率播放, 直接输出
- A程序接着播放48000hz声音, 提交给pulseaudio
- pulseaudio查询到声卡是否支持48000hz采样率播放, 不支持则重采样到44100后输出.
如果声卡支持, 则直接输出.那么, 现在支持, 好, 设定声卡为48000模式, 然后直接输出. - A程序播放48000声音的同时, B程序开始播放44100的声音.
- pulseaudio将B程序的声音重采样到48000, 然后混音输出.
- A程序退出, B程序继续播放44100的声音
- pulseaudio将B程序的声音重采样到48000, 然后混音输出.
- B程序暂停或停止播放, 然后重新或继续播放44100的声音
- pulseaudio将设定声卡为44100模式, 然后直接输出.
- A程序又播放48000声音
- pulseaudio将A程序的声音重采样到44100, 然后混音输出.
- 用户切换到蓝牙耳机.只支持32000hz采样率.
- pulseaudio将A程序的48000hz重采样到32000hz, B的44100重采样到32000hz, 混音后输出给蓝牙耳机.
-
同样是系统混音, pulseaudio支持动态采样率调节.依据当前声卡支持的采样率和当前程序提交的数据,
动态修改内部混音的采样率.尽量减小不必要的重采样过程
-
顺便提一下:google的Android不使用pulseaudio, 自己搞了个AudioFlinger,
不支持动态采样率, 还是会强制重采样到44100hz.
ALSA Project
ALSA 高级Linux音频架构
ALSA体系结构
- alsa-lib
- Control Interface (/dev/snd/controlCX)控制接口:提供管理声卡注册和请求可用设备的通用功能
- PCM Interface (/dev/snd/pcmCXDX) PCM接口:管理数字音频回放(playback)和录音(capture)的接口。它是开发数字音频程序最常用到的接口。
- Raw MIDI Interface (/dev/snd/midiCXDX) Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),标准的电子乐器。这些API提供对声卡上MIDI总线的访问。这个原始接口基于MIDI事件工作,由程序员负责管理协议以及时间处理。
- Timer Interface (/dev/snd/timer) 定时器(Timer)接口:为同步音频事件提供对声卡上时间处理硬件的访问。
- Sequencer Interface (/dev/snd/seq)时序器(Sequencer)接口
- Mixer Interface (/dev/snd/mixerCXDX) 混音器(Mixer)接口
- alsa-utils 一些常用有用的工具
- amixer:可以配置底层提供的alsa接口。
amixer contents
可以查看所有的控制接口和当前值amixer controls
可以列出所有的接口amixer cget (接口名)
可以获取指定接口值amixer cset (接口名)
可以设置指定接口值
- aplay/arecord 用于放音和录音的应用
- alsactl: 用于保存alsa配置
- alsamixer: alsamixer程序是amixer的ncurses的版本。
- aconnect, aseqnet 是进行MIDI连接和查看连接端口列表。
- alsa-plugins
- alsa-tools
- …
环境搭建
kernel基于mozart linux-3.0.8 [speaker-master, commit id:8334430651460305e884496d9356b8b6f132ebaa]
板子使用 SP_M150_V2.0
- Sound card support
- [*] Advanced Linux Sound Architecture —>
- [*] ALSA for SoC audio support —>
- [*] ASoC support for Ingenic —>
- jz board type select —>
- soc 4775 codec type select (Audio support for wattle with internal codec) = Audio support for wattle with internal codec
- [*] Support DMIC for record
- [*] JZ audio dma clear auto dirty memor
- [*] JZ audio dma cyclic dma with hrtimer callback mode
- [] Open Sound System (DEPRECATED) —>
测试
- 先使用amixer contents查看接口, 并用amixer cset 设置对应的值
- 使用aplay播放wav音频, 只支持wav格式
- 使用arecord 录音
ALSA混音
Alsa本身也提供混音的plugin,dmix.
应用程序不需要修改,添加配置dmix的/etc/asound.conf配置文件即可
效果是可以有多个应用同时放音
结构如下:
Hardware
ALSA编程
内核Alsa代码进行改动时会影响到应用程序的代码编写
ALSA放音
详细实例见: mozart_sdk/sources/application/molib_src/trunk/file-manager/player/mp3_utils.c
snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, channels);
snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
snd_pcm_hw_params(handle, params);
while(flag){
...
snd_pcm_writei (handle, Output, n);
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
ALSA录音
详细实例见: mozart_sdk/sources/application/molib_src/trunk/utils/librecord/alsa/record.c
ALSA配置参数
有关调节音量,设置静音,详细实例见: /mozart_sdk/sources/application/molib_src/trunk/utils/volume/volume_alsa.c
char *card = "default";
snd_ctl_t *handle = NULL;
snd_ctl_elem_value_t *ctl_elem_value;
snd_ctl_open(&handle, card, 0);
snd_ctl_elem_value_malloc (&ctl_elem_value);
snd_ctl_elem_value_set_numid (ctl_elem_value, 11);
snd_ctl_elem_value_set_interface (ctl_elem_value, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_value_set_name (ctl_elem_value, "Digital Playback mute");
snd_ctl_elem_value_set_integer(ctl_elem_value, 0, val);
snd_ctl_elem_write(handle, ctl_elem_value);
snd_ctl_close(handle);
snd_ctl_elem_value_free (ctl_elem_value);
参考