版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/jingxia2008/article/details/26701899

在 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 设备打开配置功能,可是详细平台却并不一定都能正常运行,主要是这些库实现都非常easy,没有考虑全面,你的硬件细节可能刚好被他们忽略了。相同以我们的 wm8904 来说,我们不支持 time stamp ,可是 tinyalsa 是默认打开的必须将其关掉。

    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 int device, 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 驱动project师依据硬件设计和芯片手冊,逐个查证。配置到一个音频系统工作的最佳状态。

那么用户体验才干最好。

Android 音频系统分析

下面分析基于 4.4.2

audio HAL

tinyalsa 与 audioroute

Android 音频系统基于 Linux 的 ALSA 驱动, tinyalsa 在 alsa 的驱动基础上封装音频接口。提供给 audio HAL。 audio HAL 提供接口给 Android audioflinger 等 framework。

HAL 须要实现 audio 硬件的打开与关闭(这里是 android 觉得的硬件)。
static inline int audio_hw_device_open(const struct hw_module_t* module,
struct audio_hw_device** device)
{
return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
(struct hw_device_t**)device);
} static inline int audio_hw_device_close(struct audio_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 的操作:
/* 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 能够查看和配置音频路径:

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 音频server。它会载入 audio hal ,并处理 audio 应用发出音频请求。这个分析的有非常多,參考下面:

Android 4.4 Kitkat 音频实现及简要分析的更多相关文章

  1. Android 关于 OnScrollListener 事件顺序次数的简要分析

    在 Android 的 OnScrollListener 整个事件我主要分析下他的执行顺序: 实现滚动事件的监听接口 new AbsListView.OnScrollListener(){ @Over ...

  2. Android 4.4 KitKat 新特性

    New in Android 4.4 KitKat 本文是一个概览,关于KitKat,也即Android4.4的新东西,先是功能型的,之后是设计上的. 很多特性本文并没有提到,很多提到的特性也只是简短 ...

  3. [转]Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划

    转自:Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划 前面我们从Android应用程序与SurfaceFlinger服务的关系出发,从侧面简单学习了Surfa ...

  4. Android 4.4 KitKat, the browser and the Chrome WebView

    Having V8 as the JavaScript engine for the new web view, the JavaScript performance if much better, ...

  5. 让你的短信应用迎接Android 4.4(KitKat)

    原文地址:Getting Your SMS Apps Ready for KitKat 发送和接收短信是手机最基本的功能,很多的开发者也开发了很多成功的应用来增强Android这一方面的体验.你们当中 ...

  6. Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 10

    今天编译一个project,我设置为api 14,可是编译报错: Using 1.7 requires compiling with Android 4.4 (KitKat); currently u ...

  7. Android 4.4(KitKat)中VSync信号的虚拟化

    原文地址:http://blog.csdn.net/jinzhuojun/article/details/17293325 Android 4.1(Jelly Bean)引入了Vsync(Vertic ...

  8. Android应用程序的Activity启动过程简要介绍和学习计划

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6685853 在Android系统中,Activ ...

  9. Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

随机推荐

  1. 比isConnected()更靠谱的的获取socket实时连接状态!

    看到这个标题,预计非常多人会说用socket.isConnected()或者socket.isClosed()等方法来推断即可了,但其实这些方法都是訪问socket在内存驻留的状态,当socket和s ...

  2. java线程不安全类与写法

    线程不安全类 1.为什么java里要同时提供stringbuilder和stringbuffer两种字符串拼接类 2.simpledateformate是线程不安全的类,如果把它作为全局变量会有线程安 ...

  3. Python内置函数之chr()

    参数是一个整型对象: chr(integer) 返回整型对应的Unicode字符对象. 例子: >>> chr() '\x0c' >>> chr() 'ⲓ' > ...

  4. 键值对集合 dict(字典)

    xx= { ss, ss } 创建字典 len(ss) 返回字典到长度,len函数可以返回任何集合的长度,list.tuple.dict都是集合的一种 什么是dict 我们已经知道,list 和 tu ...

  5. Vue 组件 非父子组件通信

    有时候两个组件也需要通信(非父子关系),在简单的场景下,可以使用一个空的vue实例作为中央事件总线: var bus = new Vue(); //触发组件a中的事件 bus.$emit('id-se ...

  6. spring 第一篇(1-2):管理你的beans

    在基于spring的应用中,你的应用对象存活在spring container(容器中).容器创建,将它们装配到一起.还有配置和管理它们完整的生命周期(从生到死) 下一章节,你会看到如何配置Sprin ...

  7. hdu5794 A Simple Chess 容斥+Lucas 从(1,1)开始出发,每一步从(x1,y1)到达(x2,y2)满足(x2−x1)^2+(y2−y1)^2=5, x2>x1,y2>y1; 其实就是走日字。而且是往(n,m)方向走的日字。还有r个障碍物,障碍物不可以到达。求(1,1)到(n,m)的路径条数。

    A Simple Chess Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)To ...

  8. C#网络编程系列文章(五)之Socket实现异步UDPserver

    原创性声明 本文作者:小竹zz 本文地址http://blog.csdn.net/zhujunxxxxx/article/details/44258719 转载请注明出处 文章系列文件夹 C#网络编程 ...

  9. (C#)System.Security.SecureString(表示应保密的文本)

    正常的String类型值,在脱离开作用域之后,其值在内存中并不会被立即销毁,这时如果有人恶意扫描你的内存,程序中所保存的机密信息就会暴露;于是就有了System.Security.SecureStri ...

  10. Java 学习笔记之读取jdbc.propertyes配置参数

    package test; import java.io.IOException; import java.io.InputStream; import java.util.Properties; p ...