当前位置: 代码迷 >> Android >> Android 4.4 Kitkat 音频实现及简明分析
  详细解决方案

Android 4.4 Kitkat 音频实现及简明分析

热度:98   发布时间:2016-04-24 12:02:01.0
Android 4.4 Kitkat 音频实现及简要分析

关注微信号:javalearns?? 随时随地学Java

或扫一扫

?

?

随时随地学Java

在 Android 4.4 上实现录放音
?
背景
Android 自 ICS 开始,音频系统就有了很大的变化,先是抛弃了 alsalib,然后是采用了 AIO,各级框架上,都有了自己的特色,与 Linux 的音频应用渐行渐远,形成了自己独特的音频管理和音频配置功能。总的来说改进还是非常大,至少在用户体验上已经大大的超越了之前的版本。我们就从 4.4 的音频实现上来分析其中的一些变化和实现机制。
?
?
要求
?
首先是硬件功能正常,这个不表。 Linux 支持 alsa 驱动,生成 alsa 子系统,最好是能够在 buildroot 等其他文件系统上事先测试音频的播放和录制。
?
HAL
音频的 HAL 简单实现,参考 device/asus/grouper/audio?, 如果没有太复杂的音频配置,基本上可以在这个代码基础上稍微修改,主要是一些播放和录制的参数。这个 HAL 已经实现了通用的接口,并且调用的也是标准的 tinyalsa 的接口,移植性非常高。我们这里使用的 wm8904,功能不多,直接使用即可。
?
?
Android 的配置
?
主要是4个文件?audio_policy.conf ?media_profiles.xml media_codecs.xml mixer_paths.xml 参考 asus ,不必大改,基本照抄,完全可以直接使用,开源万岁。
?
做好文件系统,这个时候系统应该就不使用 default 的 stub 音频 HAL , 而是用我们添加的?audio?HAL 了。 但是能否发声,这个多半还是不能。
?
调试
?
audio?系统调用了 ?libtinyalsa libaudioutils libaudioroute 几个底层库。 这几个移植了一些通用的 alsa 设备打开配置功能,但是具体平台却并不一定都能正常执行,主要是这些库实现都很简单,没有考虑全面,你的硬件细节可能刚好被他们忽略了,同样以我们的 wm8904 来说,我们不支持 time stamp ,但是 tinyalsa 是默认打开的必须将其关掉。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
?????
????????disable tstamp?for?wm8904.
?????
????Change-Id: Ia22aa6ed39ede6214657487344d0488be93e5544
?
diff --git a/pcm.c b/pcm.c
index?4501777..94cf6ee?100644
--- a/pcm.c
+++ b/pcm.c
@@ -691,7?+691,7?@@ struct pcm *pcm_open(unsigned?int?card, unsigned?intdevice,
??
??
?????memset(&sparams,?0, sizeof(sparams));
-??? sparams.tstamp_mode = SNDRV_PCM_TSTAMP_ENABLE;
+??? sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
?????sparams.period_step =?1;
??
?????if?(!config->start_threshold) {
????
具体哪些参数不对,或者不合适,就需要 Android 驱动工程师根据硬件设计和芯片手册,逐个查证,配置到一个音频系统工作的最佳状态。那么用户体验才能最好。
?
?
?
Android 音频系统分析
以下分析基于 4.4.2
?
audio?HAL
?
tinyalsa 与?audioroute
Android 音频系统基于 Linux 的 ALSA 驱动, tinyalsa 在 alsa 的驱动基础上封装音频接口,提供给?audioHAL,?audio?HAL 提供接口给 Android?audioflinger 等?framework。
HAL 需要实现?audio?硬件的打开与关闭(这里是?android?认为的硬件)。
?
1
2
3
4
5
6
7
8
9
10
11
12
?????
????static?inline?int?<a title="audio"?href="http://www.android-study.com/duomeitijishu/577.html">audio</a>_hw_device_open(const?struct hw_module_t* module,
???????????????????????????????????????struct <a title="audio"href="http://www.android-study.com/duomeitijishu/577.html">audio</a>_hw_device** device)
{
????return?module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
?????????????????????????????????(struct hw_device_t**)device);
}
?
static?inline?int?<a title="audio"?href="http://www.android-study.com/duomeitijishu/577.html">audio</a>_hw_device_close(struct <a title="audio"?href="http://www.android-study.com/duomeitijishu/577.html">audio</a>_hw_device* device)
{
????return?device->common.close(&device->common);
}
需要实现 in 和 out 的 数据流
struct?audio_stream_out ? struct?audio_stream_in?
in 主要有 read 方法用于读取音频数据, ? out 主要有 write 方法,写入数据到设备,分别实现录音和放音。
详见: hardware/libhardware/include/hardware/audio.h
?
其中的方法又是调用的 tinyalsa 的接口,关于 pcm 的操作:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
?????
????/* Open and close a stream */
struct pcm *pcm_open(unsigned int card, unsigned int device,
?????????????????????unsigned int flags, struct pcm_config *config);
int pcm_close(struct pcm *pcm);
int pcm_is_ready(struct pcm *pcm);
?
/* Obtain the parameters for a PCM */
struct pcm_params *pcm_params_get(unsigned int card, unsigned int device,
??????????????????????????????????unsigned int flags);
void pcm_params_free(struct pcm_params *pcm_params);
unsigned int pcm_params_get_min(struct pcm_params *pcm_params,
????????????????????????????????enum pcm_param param);
unsigned int pcm_params_get_max(struct pcm_params *pcm_params,
????????????????????????????????enum pcm_param param);
?
/* Set and get config */
int pcm_get_config(struct pcm *pcm, struct pcm_config *config);
int pcm_set_config(struct pcm *pcm, struct pcm_config *config);
?
/* Returns a human readable reason for the last error */
const char *pcm_get_error(struct pcm *pcm);
?
/* Returns the sample size in bits for a PCM format.
?* As with ALSA formats, this is the storage size for the format, whereas the
?* format represents the number of significant bits. For example,
?* PCM_FORMAT_S24_LE uses 32 bits of storage.
?*/
unsigned int pcm_format_to_bits(enum pcm_format format);
?
/* Returns the buffer size (int frames) that should be used for pcm_write. */
unsigned int pcm_get_buffer_size(struct pcm *pcm);
unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);
unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);
?
/* Returns the pcm latency in ms */
unsigned int pcm_get_latency(struct pcm *pcm);
?
/* Returns available frames in pcm buffer and corresponding time stamp.
?* The clock is CLOCK_MONOTONIC if flag PCM_MONOTONIC was specified in pcm_open,
?* otherwise the clock is CLOCK_REALTIME.
?* For an input stream, frames available are frames ready for the
?* application to read.
?* For an output stream, frames available are the number of empty frames available
?* for the application to write.
?*/
int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail,
???????????????????????struct timespec *tstamp);
?
/* Write data to the fifo.
?* Will start playback on the first write or on a write that
?* occurs after a fifo underrun.
?*/
int pcm_write(struct pcm *pcm, const void *data, unsigned int count);
int pcm_read(struct pcm *pcm, void *data, unsigned int count);
?
/*
?* mmap() support.
?*/
int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);
int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);
int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset,
???????????????????unsigned int *frames);
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);
?
/* Start and stop a PCM channel that doesn't transfer data */
int pcm_start(struct pcm *pcm);
int pcm_stop(struct pcm *pcm);
?
/* Interrupt driven API */
int pcm_wait(struct pcm *pcm, int timeout);
?
/* Change avail_min after the stream has been opened with no need to stop the stream.
?* Only accepted if opened with PCM_MMAP and PCM_NOIRQ flags
?*/
int?pcm_set_avail_min(struct pcm *pcm,?int?avail_min);
值得一提的是 HAL 现在不包含 route 的操作,?audio?route 交给了 libaudioroute.so , 它也是调用 tinyalsa 的接口,并包含一个 xml 解析器,解析 mixer_paths.xml 里面的 route 配置数据。这样系统就可以对 alsa 的 pcm 和 mixer 进行操作了,理论上应该可以放音了,使用 tinyalsa 提供的工具可以进行测试,当然无法测试 HAL 的接口。
tinycap ? ? ?tinymix ? ? ?tinypcminfo ?tinyplay
?
tinyplay 可以播放 wav 格式的纯音频数据。 tinymix 可以查看和配置音频路径:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
?????
????root@sama5d3:/ # tinymix??????????????????????????????????????????????????????
Mixer name:?'wm8904 @ SAMA5D3EK'
Number of controls:?41
ctl???? type??? num???? name???????????????????????????????????? value
0???????INT?????1???????EQ1 Volume???????????????????????????????12
1???????INT?????1???????EQ2 Volume???????????????????????????????12
2???????INT?????1???????EQ3 Volume???????????????????????????????12
3???????INT?????1???????EQ4 Volume???????????????????????????????12
4???????INT?????1???????EQ5 Volume???????????????????????????????12
5???????INT?????2???????Digital Capture Volume???????????????????96?96
6???????ENUM????1???????Left Caputure Mode?????????????????????? Single-Ended
7???????ENUM????1???????Right Capture Mode?????????????????????? Single-Ended
8???????INT?????2???????Capture Volume???????????????????????????5?5
9???????BOOL????2???????Capture Switch?????????????????????????? Off Off
10??????BOOL????1???????High Pass Filter Switch????????????????? On
11??????ENUM????1???????High Pass Filter Mode??????????????????? Hi-fi
12??????BOOL????1???????ADC 128x OSR Switch????????????????????? On
13??????INT?????1???????Digital Playback Boost Volume????????????0
14??????INT?????2???????Digital Playback Volume??????????????????96?96
15??????INT?????2???????Headphone Volume?????????????????????????45?45
16??????BOOL????2???????Headphone Switch???????????????????????? On On
17??????BOOL????2???????Headphone ZC Switch????????????????????? On On
18??????INT?????2???????Line Output Volume???????????????????????57?57
19??????BOOL????2???????Line Output Switch?????????????????????? On On
20??????BOOL????2???????Line Output ZC Switch??????????????????? On On
21??????BOOL????1???????EQ Switch??????????????????????????????? Off
22??????BOOL????1???????DRC Switch?????????????????????????????? Off
23??????ENUM????1???????DRC Path???????????????????????????????? ADC
24??????BOOL????1???????DAC OSRx2 Switch???????????????????????? Off
25??????BOOL????1???????DAC Deemphasis Switch??????????????????? Off
26??????INT?????2???????Digital Sidetone Volume??????????????????0?0
27??????ENUM????1???????LINER Mux??????????????????????????????? DAC
28??????ENUM????1???????LINEL Mux??????????????????????????????? DAC
29??????ENUM????1???????HPR Mux????????????????????????????????? DAC
30??????ENUM????1???????HPL Mux????????????????????????????????? DAC
31??????ENUM????1???????Right Sidetone?????????????????????????? None
32??????ENUM????1???????Left Sidetone??????????????????????????? None
33??????ENUM????1???????DACR Mux???????????????????????????????? Right
34??????ENUM????1???????DACL Mux???????????????????????????????? Left
35??????ENUM????1???????AIFOUTR Mux????????????????????????????? Right
36??????ENUM????1???????AIFOUTL Mux????????????????????????????? Left
37??????ENUM????1???????Right Capture Inverting Mux????????????? IN1R
38??????ENUM????1???????Right Capture Mux??????????????????????? IN2R
39??????ENUM????1???????Left Capture Inverting Mux?????????????? IN1L
40??????ENUM????1???????Left Capture Mux???????????????????????? IN2L
?
????
audioflinger
?
audioflinger 是?audio?音频服务器,它会加载?audio?hal ,并处理?audio?应用发出音频请求。

关注微信号:javalearns?? 随时随地学Java

或扫一扫

?

?

随时随地学Java

  相关解决方案