Linux/Android 音频驱动从概念到 APP

这里写自定义目录标题

  • 前言
  • 硬件介绍
    • Codec 通用结构
      • ADC 框图
      • DAC 框图
      • 常用数字接口
      • 其他相关术语
    • Codec 实际结构
      • 硬件原理图
      • 芯片手册框图
    • 软硬件对应示例
      • Codec 硬件逻辑
    • Codec Linux 抽象
  • 软件介绍
  • Linux
    • Alsa 框架
      • 框架图
      • 设备中的文件结构
      • Linux 相关代码路径
    • 标准 Alsa 驱动编写
      • 编写标准 Alsa 驱动流程
      • 代码示例
    • Alsa-soc 框架及驱动编写
      • 针对硬件框架抽象
      • 相关软件框架实现
        • Machine
        • Platform
        • Codec
        • 用户空间接口定义示例
          • 不带 DPM
          • 带 DPM
  • 内核-用户控制交互
    • tinymix
    • tinypcminfo
    • tinyplay
    • 安卓中使用
  • Android
    • 整体框架图
      • 相关类及概念流程介绍
        • AudioFlinger
        • AudioPolicyService
    • Audio 管理
    • Audio 播放
      • AudioTrack 播放流程涉及类关系
      • Audio 播放数据流图
      • 相关播放线程
    • Audio 录音
    • Audio 音效
    • apk 使用 AudioTrack
    • 杂项流程记录
  • 相关参考资料

前言

	本文仅仅是个人的一个学习过程总结,并没有细化下去,如果想要细化研究,可百度查找相关资料细看。
	大概会按照:
		概念 -> 硬件 -> 软件 -> 驱动 -> 用户空间 -> Android 这种流程介绍

硬件介绍

Codec 通用结构

	下面展示了一般 Codec 芯片的框架及所可能拥有的模块图

ADC 框图

仅简单介绍了常用功能,具体含义可百度,写太多会抓不住重点

MIC: 麦克风
ADC: 模数转换
Linux/Android 音频驱动从概念到 APP_第1张图片
Linux/Android 音频驱动从概念到 APP_第2张图片

DAC 框图

DAC: 数模转换
Linux/Android 音频驱动从概念到 APP_第3张图片
Linux/Android 音频驱动从概念到 APP_第4张图片

常用数字接口

  1. IIS
  2. PCM
  3. SPDIF
    Linux/Android 音频驱动从概念到 APP_第5张图片

其他相关术语

Linux/Android 音频驱动从概念到 APP_第6张图片

Codec 实际结构

供参考理解上面介绍的相关概念

硬件原理图

取自 Mini2440 手册,数字接口为:IIS+L3
Linux/Android 音频驱动从概念到 APP_第7张图片

芯片手册框图

芯片为 UD1341TS
Linux/Android 音频驱动从概念到 APP_第8张图片
Linux/Android 音频驱动从概念到 APP_第9张图片

软硬件对应示例

下面是个更详细的例子,介绍了 WM9883 音频芯片逻辑通路及相关 Linux Codec 通路抽象

Codec 硬件逻辑

取自 wm8993 芯片手册
Linux/Android 音频驱动从概念到 APP_第10张图片

Codec Linux 抽象

Linux/Android 音频驱动从概念到 APP_第11张图片

软件介绍

Linux 部分参考资料:
韦东山一期/三期声卡相关视频
http://blog.csdn.net/droidphone Alsa 相关博客

Android 部分参考:
深入理解Android内核设计思想_林学森./第 13 章 应用不再同质化 – 音频系统
深入剖析 Android 系统_杨长刚/第 14 章 Audio
深入理解 Android 卷1_邓凡平/ 第7章 深入理解 Audio 系统

Linux

不会写太细,提纲擎领而已

Alsa 框架

框架图

Linux/Android 音频驱动从概念到 APP_第12张图片

设备中的文件结构

Linux/Android 音频驱动从概念到 APP_第13张图片

Linux 相关代码路径

Linux/Android 音频驱动从概念到 APP_第14张图片

标准 Alsa 驱动编写

	怎么写 ALSA 声卡驱动:
            1. snd_card_create()        // 创建 snd_card 的一个实例
                    snd_ctl_create()
            2. 初始化:创建声卡的功能部件(逻辑设备)
                    snd_pcm_new() 
            3. snd_card_register()      //注册声卡

编写标准 Alsa 驱动流程

		1. struct snd_card 结构体 
        1.1. snd_card 是什么
            snd_card 可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声
        音相关的逻辑设备都是在 snd_card 的管理之下,声卡驱动的第一个动作通常就是创建一个 snd_card 结构体。正因
        为如此,本节中,我们也从 struct cnd_card 开始吧。

        1.2. snd_card的定义
            snd_card的定义位于改头文件中: include/sound/core.h
       
            /* main structure for soundcard */

            struct snd_card {
                int number;                                   / * number of soundcard (index to snd_cards) * /

                char id[16];                                  / * id string of this card *                   /
                char driver[16];                              / * driver name *                              /
                char shortname[32];                           / * short name of this soundcard *             /
                char longname[80];                            / * name of this soundcard *                   /
                char mixername[80];                           / * mixer name *                               /
                char components[128];                         / * card components delimited with space *     /
                struct module *module;                        / * top‐level module *                         /

                void *private_data;                           / *【声卡的私有数据,可以在创建声卡时通过参数指定数据的大小】/
                void (*private_free) (struct snd_card *card); / * callback for freeing of private data *     /
                struct list_head devices;                     / *【记录该声卡下所有逻辑设备的链表】          /
                unsigned int last_numid;                      / * last used numeric ID *                     /
                struct rw_semaphore controls_rwsem;           / * controls list lock *                       /
                rwlock_t ctl_files_rwlock;                    / * ctl_files list lock *                      /
                int controls_count;                           / * count of all controls *                    /
                int user_ctl_count;                           / * count of all user controls *               /
                struct list_head controls;                    / *【记录该声卡下所有的控制单元的链表】        /
                struct list_head ctl_files;                   / * active control files *                     /

                struct snd_info_entry *proc_root;             / * root for soundcard specific files *        /
                struct snd_info_entry *proc_id;               / * the card id *                              /
                struct proc_dir_entry *proc_root_link;        / * number link to real id *                   /

                struct list_head files_list;                  / * all files associated to this card *        /
                struct snd_shutdown_f_ops *s_f_ops;           / * file operations in the shutdown
                state *                                       /
                spinlock_t files_lock;                        / * lock the files for this card *             /
                int shutdown;                                 / * this card is going down *                  /
                int free_on_last_close;                       / * free in context of file_release *          /
                wait_queue_head_t shutdown_sleep;
                struct device *dev;                           / * device assigned to this card *             /
                #ifndef CONFIG_SYSFS_DEPRECATED
                struct device *card_dev;                      / * cardX object for sysfs *                   /
                #endif

                #ifdef CONFIG_PM
                unsigned int power_state;                     / * power state *                              /
                struct mutex power_lock;                      / * power lock *                               /
                wait_queue_head_t power_sleep;
                #endif

                #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
                struct snd_mixer_oss *mixer_oss;
                int mixer_oss_change_count;
                #endif
            };


    2. 声卡的建立流程
        2.1.1. 第一步,创建snd_card的一个实例
        
                struct snd_card *card;
                int err;
                ....
                err = snd_card_create(index, id, THIS_MODULE, 0, &card);

            index      :一个整数值,该声卡的编号
            id         :字符串,声卡的标识符
            第四个参数 :该参数决定在创建 snd_card 实例时,需要同时额外分配的私有数据的大小,该数据的指针最
                           终会赋值给 snd_card 的 private_data 数据成员
            card       :返回所创建的 snd_card 实例的指针    

        2.1.2. 第二步,创建声卡的芯片专用数据
                声卡的专用数据主要用于存放该声卡的一些资源信息,例如中断资源、 io资源、 dma资源等。可以有两种
            创建方法:
            
                1. 通过上一步中 snd_card_create() 中的第四个参数,让 snd_card_create() 自己创建
            
                    // struct mychip 用于保存专用数据    
                    err = snd_card_create(index, id, THIS_MODULE, sizeof(struct mychip), &card);
                    // 从 private_data 中取出
                    struct mychip *chip = card‐>private_data;

                2. 自己创建:
                    struct mychip {
                        struct snd_card *card;
                        ....
                    };

                    struct snd_card *card;
                    struct mychip *chip;
                    
                    chip = kzalloc(sizeof(*chip), GFP_KERNEL);
                        ......
                    err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
                    // 专用数据记录snd_card实例
                    chip‐>card = card;
                    .....

                    然后,把芯片的专有数据注册为声卡的一个低阶设备:
                        static int snd_mychip_dev_free(struct snd_device *device)
                        {
                            return snd_mychip_free(device‐>device_data);
                        }
                        
                        static struct snd_device_ops ops = {
                            .dev_free = snd_mychip_dev_free,
                        };
                        ....
                        snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

                    注册为低阶设备主要是为了当声卡被注销时,芯片专用数据所占用的内存可以被自动地释放。

        2.1.3. 第三步,设置 Driver 的 ID 和名字

            strcpy(card‐>driver, "My Chip");
            strcpy(card‐>shortname, "My Own Chip 123");
            sprintf(card‐>longname, "%s at 0x%lx irq %i",
            card‐>shortname, chip‐>ioport, chip‐>irq);
            
            snd_card 的 driver 字段保存着芯片的ID字符串, user 空间的 alsa-lib 会使用到该字符串,所以必须要保证该 ID 的唯
            一性。 shortname 字段更多地用于打印信息, longname 字段则会出现在 /proc/asound/cards 中。

        2.1.4. 第四步,创建声卡的功能部件(逻辑设备),例如PCM, Mixer, MIDI等
            这时候可以创建声卡的各种功能部件了,还记得开头的 snd_card 结构体的 devices 字段吗?每一种部件的创建最终
            会调用 snd_device_new() 来生成一个 snd_device 实例,并把该实例链接到 snd_card 的 devices 链表中。
            通常, alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),比如:
                PCM     -- snd_pcm_new()
                RAWMIDI -- snd_rawmidi_new()
                CONTROL -- snd_ctl_create()
                TIMER   -- snd_timer_new()
                INFO    -- snd_card_proc_new()
                JACK    -- snd_jack_new()

        2.1.5. 第五步,注册声卡

            err = snd_card_register(card);
                if (err < 0) {
                snd_card_free(card);
                return err;
            }

代码示例

/sound/arm/pxa2xx-ac97.c的部分代码贴上来:

	static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)  
            {  
                struct snd_card *card;  
                struct snd_ac97_bus *ac97_bus;  
                struct snd_ac97_template ac97_template;  
                int ret;  
                pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;  
              
                if (dev->id >= 0) {  
                    dev_err(&dev->dev, "PXA2xx has only one AC97 port./n");  
                    ret = -ENXIO;  
                    goto err_dev;  
                }  
            (1)  第一步,创建snd_card的一个实例
                ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,THIS_MODULE, 0, &card);
                if (ret < 0)  
                    goto err;  
              
                card->dev = &dev->dev;  
            (3)  第三步,设置Driver的ID和名字
                strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));  
              
            (4)  第四步,创建声卡的功能部件(逻辑设备)PCM
                ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);  
                if (ret)  
                    goto err;  
            (2)  第二步,创建声卡的芯片专用数据
                ret = pxa2xx_ac97_hw_probe(dev);  
                if (ret)  
                    goto err;  
              
            (4)  第四步,创建声卡的功能部件(逻辑设备)AC97_BUS
                ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);  
                if (ret)  
                    goto err_remove;  
                memset(&ac97_template, 0, sizeof(ac97_template));  
                ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);  
                if (ret)  
                    goto err_remove;  
            (3)  第三步,设置Driver的ID和名字
                snprintf(card->shortname, sizeof(card->shortname),  
                     "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));  
                snprintf(card->longname, sizeof(card->longname),  
                     "%s (%s)", dev->dev.driver->name, card->mixername);  
              
                if (pdata && pdata->codec_pdata[0])  
                    snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);  
                snd_card_set_dev(card, &dev->dev);  
            (5)  第五步,注册声卡
                ret = snd_card_register(card);  
                if (ret == 0) {  
                    platform_set_drvdata(dev, card);  
                    return 0;  
                }  
              
            err_remove:  
                pxa2xx_ac97_hw_remove(dev);  
            err:  
                if (card)  
                    snd_card_free(card);  
            err_dev:  
                return ret;  
            }  
              
            static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)  
            {  
                struct snd_card *card = platform_get_drvdata(dev);  
              
                if (card) {  
                    snd_card_free(card);  
                    platform_set_drvdata(dev, NULL);  
                    pxa2xx_ac97_hw_remove(dev);  
                }  
              
                return 0;  
            }  
              
            static struct platform_driver pxa2xx_ac97_driver = {  
                .probe      = pxa2xx_ac97_probe,  
                .remove     = __devexit_p(pxa2xx_ac97_remove),  
                .driver     = {  
                    .name   = "pxa2xx-ac97",  
                    .owner  = THIS_MODULE,  
            #ifdef CONFIG_PM  
                    .pm = &pxa2xx_ac97_pm_ops,  
            #endif  
                },  
            };  
              
            static int __init pxa2xx_ac97_init(void)  
            {  
                return platform_driver_register(&pxa2xx_ac97_driver);  
            }  
              
            static void __exit pxa2xx_ac97_exit(void)  
            {  
                platform_driver_unregister(&pxa2xx_ac97_driver);  
            }  
              
            module_init(pxa2xx_ac97_init);  
            module_exit(pxa2xx_ac97_exit);  
              
            MODULE_AUTHOR("Nicolas Pitre");  
            MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");  

Alsa-soc 框架及驱动编写

ASOC 是建立在 Alsa 驱动层之上的优化,模块化更好,个人感觉更方便偷懒了。。。

	它在 ALSA 中又抽象出来三部分:
            1. machine: 单板相关,表明 platform 是哪个,CPU DAI 是哪个,DMA 是哪个 
                                  表明 codec 是哪个,codec DAI 是哪个 
                                  代表结构体:snd_soc_card/snd_soc_dai_link 
            2. platform: CPU 相关,主要有两方面:
                        1> DAI: 控制接口,代表数据结构 snd_soc_dai_driver
                        2> DMA: 传数据,代表数据结构 snd_soc_platform_driver
            3. codec: 声卡芯片相关,也分两方面:
                        1> DAI: 用于传输数据,代表结构 snd_soc_dai_driver
                        2> 控制接口:用于寄存器设置什么的,代表结构 snd_soc_codec_driver
        相关的数据结构内容为:
            1. machine: 
                        snd_soc_dai_link{各子驱动的名字}
            2. codec: 
                        snd_soc_dai_driver{
                            属性:声道数,采样率,格式
                            ops: 启动、关闭、参数设置
                        }
                        snd_soc_codec_driver{函数}
            3. platform:
                        dai: snd_soc_dai_driver{
                            属性:声道数,采样率,格式
                            ops: 启动、关闭、参数设置
                        }
                        dma: snd_soc_platform_driver{
                            函数 
                            ops: 数据传输相关函数 
                        }

针对硬件框架抽象

Linux/Android 音频驱动从概念到 APP_第15张图片

相关软件框架实现

具体可参考附件 【非常好】音频驱动框架总结.不带DPM.3.4.2.txt

Machine

	// 【machine】 相关部分
	//      主要任务:
	//          注册一个平台设备,名为 "soc-audio"
	//          设置平台设备的私有数据为 snd_soc_card 结构体 
	//              snd_soc_card: 
	//                  设置一个 snd_soc_dai_link 结构体,匹配各驱动
	//                      snd_soc_dai_link:
	//                          ################################################
	//                          # Codec 驱动: snd_soc_codec_driver/snd_soc_dai_driver
	//                          .codec_name     : 指明 Codec 名称,用于设置 Codec 寄存器,注册 snd_soc_codec_driver 两个结构体 
	//                          .codec_dai_name : 指明 Codec Dai 名称,用于与 CPU 通信的接口操作,如 PCM/I2S, 注册 snd_soc_dai_driver 
	//                          ################################################
	//                          # Platform 驱动:snd_soc_platform_driver/snd_soc_dai_driver
	//                          .platform_name  : 指明 DMA 驱动的名称,用于如何将音频数据传到 dai 接口, 注册 snd_soc_platform_driver 这个结构体
	//                          .cpu_dai_name   : 指明 CPU Dai 名称,用于与 codec 通信的接口,如 PCM/I2S 等,注册 snd_soc_dai_driver 结构体
	//                          .ops             
	//S3c24xx_uda134x.c (sound\soc\samsung)                   |                   
	module_platform_driver(s3c24xx_uda134x_driver);           |//Mach-mini2440.c (arch\arm\mach-s3c24xx)
	static struct platform_driver s3c24xx_uda134x_driver = {  |   static struct platform_device mini2440_audio = {                                                                                                                      
	    .probe  = s3c24xx_uda134x_probe,                      |      .name       = "s3c24xx_uda134x",                                                                                          
	    .remove = s3c24xx_uda134x_remove,                     |      .id     = 0,                                                                                          
	    .driver = {                                           |      .dev        = {                                                                  
	        .name = "s3c24xx_uda134x",                        |          .platform_data  = &mini2440_audio_pins,                                                                                      
	        .owner = THIS_MODULE,                             |      },                                                                                  
	    },                                                    |   };                                                          
	};                                                        |

左边:
s3c24xx_uda134x_probe()//【注册一个名为 "soc-audio" 平台设备,其私有数据保存了要注册的 snd_soc_card 】
	s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);                          
		platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x);                   
		                        /* 整个驱动核心 */                                                    
		                        static struct snd_soc_card snd_soc_s3c24xx_uda134x = {                
		                            .name = "S3C24XX_UDA134X",                                        
		                            .owner = THIS_MODULE,                                             
		                            .dai_link = &s3c24xx_uda134x_dai_link,                            
		                            .num_links = 1,                                                   
		                        };                                                                    
		                        /* 指明了用哪个 codec,codec dai,cpu,cpu dai */                        
		                        static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {           
		                            .name = "UDA134X",                                                
		                            .stream_name = "UDA134X",                                         
		                            .codec_name = "uda134x-codec",    // 【Codec】用哪一个 codec      
		                            .codec_dai_name = "uda134x-hifi", // 用 codec 芯片的哪一个 dai    
		                            .cpu_dai_name = "s3c24xx-iis",    // 【Platform】指定 2440 的 dai 
		                            .ops = &s3c24xx_uda134x_ops,                                      
		                            .platform_name  = "samsung-audio",// 2440 DMA 操作                
		                        };                                                                    
		                        static struct snd_soc_ops s3c24xx_uda134x_ops = {                     
		                            .startup = s3c24xx_uda134x_startup,  // 获得时钟                  
		                            .shutdown = s3c24xx_uda134x_shutdown,// 释放时钟                  
		                            .hw_params = s3c24xx_uda134x_hw_params,//设置 cpu dai 通信格式    
		                        }; 

右边:
// Soc-core.c (sound\soc) 匹配对应的驱动                                                                                       
 static struct platform_driver soc_driver = {                  
     .driver     = {                          
       .name       = "soc-audio",                                                
         .owner      = THIS_MODULE,                      
         .pm     = &snd_soc_pm_ops,           
     },                                                        
     .probe      = soc_probe,                 
     .remove     = soc_remove,                
 };                                                                                  
 soc_probe()                                                                                                      
       // 注册左边声明 snd_soc_card 结构的  snd_soc_s3c24xx_uda134x                                                           
       snd_soc_register_card(card);                                                                                            
             card->rtd = devm_kzalloc(card->dev,...                                                                            
             card->rtd[i].dai_link = &card->dai_link[i];  // &s3c24xx_uda134x_dai_link                                         
                                                                                                                               
             list_add(&card->list, &card_list);                                                                                
                                                   
             snd_soc_instantiate_cards();  // 实例化声卡   
                 snd_soc_instantiate_cards(card);                                                                             
                        3.1   /* bind DAIs,确定使用 CPU 侧以及 Codec 侧的哪一个 DAI  */                                       
                              for (i = 0; i < card->num_links; i++)                                                            
                                soc_bind_dai_link(card, i);                                                                    
                                    3.1.1 /* find CPU DAI */                                                                   
                                          rtd->cpu_dai = cpu_dai;       = //&s3c24xx_i2s_dai                                   
                                    3.1.2 /* find_codec */                                                                     
                                          rtd->codec = codec;           = // codec, codec->driver=&soc_codec_dev_uda134x       
                                    3.1.3 /* find CODEC DAI */                                                                 
                                          rtd->codec_dai = codec_dai;   // = &uda134x_dai                                      
                                    3.1.4 /* find_platform */                                                                  
                                          rtd->platform = platform;     // = &samsung_asoc_platform                            
                                                                                                               
         3.2 /* initialize the register cache for each available codec */                                          
             ret = snd_soc_init_codec_cache(codec, compress_type);                                                 
                                                                                                                   
         3.3 snd_card_create() // 标准声卡创建函数                                                                             
                                                                                                                   
         3.4 /* early DAI link probe */ 
             // 这个函数会调用各个匹配的驱动的 probe 函数, 会匹配哪些呢?就是  s3c24xx_uda134x_dai_link  指定的那些名字的驱动 
             soc_probe_dai_link()                                                                                    
                     /* probe the cpu_dai */                                                                         
                     /* probe the CODEC */                                                                           
                     /* probe the platform */                                                                        
                     /* probe the CODEC DAI */                                                                       
                     /* create the pcm */                                                                            
                                 ret = soc_new_pcm(rtd, num);      // 创建 PCM 部件                                            
                                                     struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;                    
                                                                 soc_pcm_ops->open   = soc_pcm_open;             
                                                                 soc_pcm_ops->close  = soc_pcm_close;            
                                                                 soc_pcm_ops->hw_params  = soc_pcm_hw_params;    
                                                                 soc_pcm_ops->hw_free    = soc_pcm_hw_free;      
                                                                 soc_pcm_ops->prepare    = soc_pcm_prepare;      
                                                                 soc_pcm_ops->trigger    = soc_pcm_trigger;      
                                                                 soc_pcm_ops->pointer    = soc_pcm_pointer;      
                                                                                                                     
                                                     snd_pcm_new()                                                             
         3.5 snd_card_register() // 标准声卡注册函数                                                                           

Platform

	// 【platform】的匹配 probe : 这是处理 CPU 的 DMA 的操作 
	//      主要作用:通过 snd_soc_register_platform() 注册平台相关的 DMA 相关操作
	//                即注册 snd_soc_platform_driver 这个结构体
	//
	// Dma.c (sound\soc\samsung)                                |//Devs.c (arch\arm\plat-samsung)                                                                                        
	static struct platform_driver asoc_dma_driver = {           | struct platform_device samsung_asoc_dma = {                      
	    .driver = {                                             |   .name       = "samsung-audio",                      
	        .name = "samsung-audio",                            |   .id     = -1,                      
	        .owner = THIS_MODULE,                               |   .dev        = {                      
	    },                                                      |       .dma_mask       = &samsung_device_dma_mask,                      
	                                                            |       .coherent_dma_mask  = DMA_BIT_MASK(32),                      
	    .probe = samsung_asoc_platform_probe,                   |   }                      
	    .remove = __devexit_p(samsung_asoc_platform_remove),    | };                      
	};                                                          |                       
	samsung_asoc_platform_probe()                                                                                     
	    return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
	                                                // 平台相关操作驱动
	                                                static struct snd_soc_platform_driver samsung_asoc_platform = {
	                                                    .ops        = &dma_ops,
	                                                    .pcm_new    = dma_new,                  // 分配 DMA 内存
	                                                    .pcm_free   = dma_free_dma_buffers,     // 释放 DMA 内存
	                                                };                                      
	                                                // DMA 操作 
	                                                static struct snd_pcm_ops dma_ops = {
	                                                    .open       = dma_open,                 // 打开 pcm 设备时调用,用于保存平台 dma 参数,获取 DMA 中断
	                                                    .close      = dma_close,
	                                                    .ioctl      = snd_pcm_lib_ioctl,
	                                                    .hw_params  = dma_hw_params,            // 获得对应的 dai 的 dma 参数
	                                                    .hw_free    = dma_hw_free,      
	                                                    .prepare    = dma_prepare,              // 正式开始数据传输时会调用该函数
	                                                    .trigger    = dma_trigger,              // 数据传送的开始、暂停、恢复和停止时调用
	                                                    .pointer    = dma_pointer,              // 返回传送数据的当前位置
	                                                    .mmap       = dma_mmap,
	                                                };
	+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                                                                                                                                                                                                                                                             
	// 【CPU DAI】 的匹配 probe: 这是处理与声卡芯片的操作的接口设置的  
	//      主要作用:通过 snd_soc_register_dai() 注册 CPU Dai 
	//                即注册 snd_soc_dai_driver 结构体
	//
	// S3c24xx-i2s.c (sound\soc\samsung)                    |// Devs.c (arch\arm\plat-samsung)
	static struct platform_driver s3c24xx_iis_driver = {    | struct platform_device s3c_device_iis = {
	    .probe  = s3c24xx_iis_dev_probe,                    |   .name       = "s3c24xx-iis",
	    .remove = __devexit_p(s3c24xx_iis_dev_remove),      |   .id     = -1,
	    .driver = {                                         |   .num_resources  = ARRAY_SIZE(s3c_iis_resource),
	        .name = "s3c24xx-iis",                          |   .resource   = s3c_iis_resource,
	        .owner = THIS_MODULE,                           |   .dev        = {
	    },                                                  |       .dma_mask       = &samsung_device_dma_mask,
	};                                                      |       .coherent_dma_mask  = DMA_BIT_MASK(32),                                        
	                                                        |   }                                      
	s3c24xx_iis_dev_probe()                                 | };                                       
	    return snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);                                                                                           
	                                            static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
	                                                .probe = s3c24xx_i2s_probe,
	                                                .suspend = s3c24xx_i2s_suspend,
	                                                .resume = s3c24xx_i2s_resume,
	                                                .playback = {
	                                                    .channels_min = 2,
	                                                    .channels_max = 2,
	                                                    .rates = S3C24XX_I2S_RATES,
	                                                    .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
	                                                .capture = {
	                                                    .channels_min = 2,
	                                                    .channels_max = 2,
	                                                    .rates = S3C24XX_I2S_RATES,
	                                                    .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
	                                                .ops = &s3c24xx_i2s_dai_ops,
	                                            };  
	                                            // cpu dai 操作,他与 codec dai 数据结构是一样的
	                                            static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
	                                                .trigger    = s3c24xx_i2s_trigger,      // 在 PCM 数据开始,传输,唤醒时被调用,用来启动/停止 IIS 传输
	                                                .hw_params  = s3c24xx_i2s_hw_params,    // 根据传入的参数,配置 CPU 的 IIS 相关模块设置
	                                                
	                                                ///
	                                                // 以下这里函数在 Machine 驱动的 hw_params() 会调用 
	                                                .set_fmt    = s3c24xx_i2s_set_fmt,
	                                                .set_clkdiv = s3c24xx_i2s_set_clkdiv,
	                                                .set_sysclk = s3c24xx_i2s_set_sysclk,
	                                            };

Codec

	// 【codec】 的匹配 probe     
	//      主要作用:通过 snd_soc_register_codec() 注册 Codec Dai /Code Driver 
	//                即 snd_soc_dai_driver 与 snd_soc_codec_driver 两个结构体
	//                                                                                                                                                
	//Uda134x.c (sound\soc\codecs)                              |// Mach-mini2440.c (arch\arm\mach-s3c24xx)
	static struct platform_driver uda134x_codec_driver = {      | static struct platform_device uda1340_codec = {
	    .driver = {                                             |       .name = "uda134x-codec",
	        .name = "uda134x-codec",                            |       .id = -1,
	        .owner = THIS_MODULE,                               | };
	    },                                                      |
	    .probe = uda134x_codec_probe,                           |
	    .remove = __devexit_p(uda134x_codec_remove),            |
	};                                                          |
	uda134x_codec_probe()                                                           
	    return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1);
	                                            
	                                            // codec 的寄存器操作: snd_soc_codec_driver
	                                             static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
	                                                .probe =        uda134x_soc_probe,      // 配置硬件,通过 snd_soc_add_codec_controls() 注册 snd_kcontrol_new 
	                                                .remove =       uda134x_soc_remove,
	                                                .suspend =      uda134x_soc_suspend,
	                                                .resume =       uda134x_soc_resume,
	                                                
	                                                // UDA1341的寄存器不支持读操作
	                                                // 要知道某个寄存器的当前值,
	                                                // 只能在写入时保存起来
	                                                .reg_cache_size = sizeof(uda134x_reg),
	                                                .reg_word_size = sizeof(u8),
	                                                .reg_cache_default = uda134x_reg,
	                                                .reg_cache_step = 1,
	                                                .read = uda134x_read_reg_cache,
	                                                .write = uda134x_write,           // 写寄存器,通过 snd_soc_wirte() 调用到 
	                                                .set_bias_level = uda134x_set_bias_level,
	                                            };  
	                                            /
	                                            // codec 的 DAI 接口设置
	                                            static struct snd_soc_dai_driver uda134x_dai = {
	                                                .name = "uda134x-hifi",
	                                                /* playback capabilities */
	                                                .playback = {
	                                                    .stream_name = "Playback",
	                                                    .channels_min = 1,
	                                                    .channels_max = 2,
	                                                    .rates = UDA134X_RATES,
	                                                    .formats = UDA134X_FORMATS,
	                                                },
	                                                /* capture capabilities */
	                                                .capture = {
	                                                    .stream_name = "Capture",
	                                                    .channels_min = 1,
	                                                    .channels_max = 2,
	                                                    .rates = UDA134X_RATES,
	                                                    .formats = UDA134X_FORMATS,
	                                                },
	                                                /* pcm operations */
	                                                .ops = &uda134x_dai_ops,
	                                            };
	                                            
	                                            /
	                                            // 以下这里函数在 Machine 驱动的 hw_params() 会调用 
	                                            static const struct snd_soc_dai_ops uda134x_dai_ops = {
	                                                .startup	= uda134x_startup,
	                                                .shutdown	= uda134x_shutdown,
	                                                .hw_params	= uda134x_hw_params,
	                                                .digital_mute	= uda134x_mute,
	                                                .set_sysclk	= uda134x_set_dai_sysclk,
	                                                .set_fmt	= uda134x_set_dai_fmt,
	                                            };

用户空间接口定义示例

不带 DPM
	################################################################################
	Mixer 控件:混音器,
	    Mixer控件用于音频通道的路由控制,由多个输入和一个输出组成,多个输入可以自由
	    地混合在一起,形成混合后的
	################################################################################
	static const struct snd_kcontrol_new left_speaker_mixer[] = {
	    SOC_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
	    SOC_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
	    SOC_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
	    SOC_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
	};
	// 以上这个 mixer 使用寄存器 WM8993_SPEAKER_MIXER 的第 3,5,6,7 位来分别控制 4 个输入端的开启和关闭
	
	################################################################################
	Mux 控件:多路开关选择器
	    Mux 中能同时只有一路被选中输出,多路开关选择器嘛
	################################################################################
	第一步,定义字符串和values数组:输入端名字
	###############################################
	static const char *drc_path_text[] = {
	    "ADC",
	    "DAC"
	};
	
	###############################################
	第二步,利用 ASoc 提供的辅助宏定义 soc_enum 结构,用于描述寄存器
	###############################################
	static const struct soc_enum drc_path = SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);
	                                        // 从左到右依次为: 【xreg】/【xshif】/【xmax】/【xtexts】
	
	###############################################
	第三步,利用 ASoc 提供的辅助宏,定义 soc_kcontrol_new 结构,该结构最后用于注册该 mux 控件
	###############################################
	static const struct snd_kcontrol_new wm8993_snd_controls[] = {
	    SOC_DOUBLE_TLV(......),                 // 定义简单型的控件,只控制一位
	    ......
	    SOC_ENUM("DRC Path", drc_path),         // 定义 Mux 控件 
	    ......
	}
带 DPM
	硬件示意图:见 WM9883 SPK 输出逻辑通路与硬件通路图
                                 
	################################################################################
	第一步,利用辅助宏定义 widget 所需要的 dapm kcontrol
	################################################################################
	    ########################################
	    # Mixer 控件:多选一,多个输入可同时选中
	    static const struct snd_kcontrol_new left_speaker_mixer[] = {
	        // 从右到右依次为:【name】/【reg】/【shift】/【max】/【invert】
	        
	        SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
	        SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
	        SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
	        SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
	        
	    };
	
	    static const struct snd_kcontrol_new right_speaker_mixer[] = {
	        SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
	        SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
	        SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
	        SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
	    };
	
	    ########################################
	    # Mux 控件:多选一,多个输入同时只有一个可选中
	    // 1. 定义字符串和 values 数组:输入端名字
	    static const char *aif_text[] = {
	        "Left", "Right"
	    };
	      
	    // 2. 利用 ASoc 提供的辅助宏定义 soc_enum 结构,用于描述寄存器,从左到右依次为: 【xreg】/【xshif】/【xmax】/【xtexts】
	    static const struct soc_enum aifinl_enum = SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);
	    static const struct soc_enum aifinr_enum = SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 14, 2, aif_text);
	    
	    
	    // 3. 利用 ASoc 提供的辅助宏,定义 soc_kcontrol_new 结构,该结构最后用于注册该 mux 控件
	    static const struct snd_kcontrol_new aifinl_mux = SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);    
	    static const struct snd_kcontrol_new aifinr_mux = SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
	
	################################################################################
	第二步,定义真正的 widget,包含第一步定义好的 dapm 控件
	################################################################################
	static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
	    ......
	    #########################################
	    # 指定 AIFINL/AIFINR 为输出流:
	    SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
	    SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
	    ......
	    #########################################
	    # 创建 Mux, 不带电源管理: DACL Mux/DACR Mux
	    SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
	    SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
	    
	    #########################################
	    # 创建 Mixer, SPKL/SPKR 带电源管理,指定电源管理寄存器及操作位
	    SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
	    SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
	    ......
	};
	
	################################################################################
	第三步,定义这些 widget 的连接路径:
	################################################################################
	static const struct snd_soc_dapm_route routes[] = {
	    ......
	    
	    // 模块        模块引脚  另一模块引脚
	    { "DACL Mux", "Left", "AIFINL" },       # AIFINL 连接到 DACL Mux 的 Left 输入脚
	    { "DACL Mux", "Right", "AIFINR" },      # AIFINL 连接到 DACR Mux 的 Left 输入脚
	    { "DACR Mux", "Left", "AIFINL" },       # AIFINR 连接到 DACL Mux 的 Right 输入脚
	    { "DACR Mux", "Right", "AIFINR" },      # AIFINR 连接到 DACR Mux 的 Right 输入脚
	    
	    ......
	    
	    { "SPKL", "DAC Switch", "DACL" },       # DACL 连接到 SPKL 的 DAC Switch 输入脚
	    { "SPKL", NULL, "CLK_SYS" },
	    
	    { "SPKR", "DAC Switch", "DACR" },       # DACR 连接到 SPKR 的 DAC Switch 输入脚
	    { "SPKR", NULL, "CLK_SYS" },
	};
	
	################################################################################
	第四步,在 codec 驱动的 probe 回调中注册这些 widget 和路径
	################################################################################
	static int wm8993_probe(struct snd_soc_codec *codec)
	{
	    ......
	    snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets,ARRAY_SIZE(wm8993_dapm_widgets));
	    ......
	    
	    snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
	    ......
	}

内核-用户控制交互

	安卓与内核相关音频交互是通过 tinyalsa 相关接口交互的,即通过 tinyalsa 相关命令,控制前面内核定义的用户接口
	所以这里简单介绍下 tinyalsa 工具,了解下有哪些接口

tinymix

	tinymix 的使用:
    # tinymix  
    Mixer name: 'audiocodec'  
    Number of controls: 12  
    ctl type    num name                                     value  
    0   INT 1   MIC1_G boost stage output mixer control     3  
    1   INT 1   MIC2_G boost stage output mixer control     3  
    2   INT 1   LINEIN_G boost stage output mixer control   3  
    3   INT 1   MIC1 boost AMP gain control                 4  
    4   INT 1   MIC2 boost AMP gain control                 4  
    5   INT 1   Lineout volume control                      31  
    6   INT 1   ADC input gain ctrl                         3  
    7   BOOL    1   Audio linein in                         On  
    8   BOOL    1   Audio lineout                           Off  
    9   BOOL    1   Audio adda drc                          Off  
    10  BOOL    1   Audio adda loop                         Off  
    11  ENUM    1   audio capture mode                      linein  
    
    
    一个 mixer 通常有多个 controler,像这个,里面有 12 个,然后就分别列出每一个 controller 的信息
        首先看第一个:它的编号为 0,类型是 int 型,它目前的值是 3,它是用来控制 mic1 的放大倍数的
        然后看第7个是一个 bool 型,也就是只有开关
        
    使用方法:
        tinymix [序号] <参数>                 // 写配置
        tinymix [序号]                        // 读配置

    如:
        tinymix  "RX1 MIX1 INP1"  RX1
        tinymix  "RX2 MIX1 INP1"  RX2
        tinymix  "RDAC2 MUX"  RX2
        tinymix  "HPHL"  Switch
        tinymix  "HPHR"  Switch

tinypcminfo

	tinypcminfo 的使用:获得支持格式类型      
    # tinypcminfo -D 0 -d 0
        Info for card 0, device 0:

        PCM out:
              Access:   0x000009
           Format[0]:   0x003ffc
           Format[1]:   00000000
         Format Name:   S16_LE, S16_BE, U16_LE, U16_BE, S24_LE, S24_BE, U24_LE, U24_BE, S32_LE, S32_BE, U32_LE, U32_BE
           Subformat:   0x000001
                Rate:   min=8000Hz      max=192000Hz
            Channels:   min=1           max=2
         Sample bits:   min=16          max=32
         Period size:   min=0           max=24576
        Period count:   min=1           max=4

        PCM in:
        cannot open device '/dev/snd/pcmC0D0c'
        Device does not exist.

tinyplay

	tinyplay 的使用:
        tinyplay file.wav [-D card] [-d device] [-p period_size] [-n n_periods] 
        
    目前 286 能用的使用方法:
        插入耳机
        tinymix 4 On   // Headset_Speaker_Amp_Switch, 这里有个问题,就是增益为 0,奇怪,需要点一下 TP 才短暂有声
        tinyplay 441.wav -D 0 -d 0 -p 2048 -n 2 

安卓中使用

	 由于 Android 中默认并没有使用标准 alsa,而是使用的是 tinyalsa,所以就算基于命令行的测试也要使用 libtinyalsa。
	Android 系统在上层 Audio 千变万化的时候,可以能这些个工具实时查看到,比如音频通道的切换等等.
	
	1.编译tinyalsa配套工具
	    $ mmm external/tinyalsa/
	
	    编译完后会产生tinyplay/tinymix/tinycap等等工具。
	        tinymix: 查看配置混音器
	        tinyplay: 播放音频
	        tinycap: 录音
	
	
	2.查看当前系统的声卡
	    root@android:/ # cat /proc/asound/cards  
	     0 [RKRK616        ]: RK_RK616 - RK_RK616  
	                          RK_RK616  
	     1 [ROCKCHIPSPDIF  ]: ROCKCHIP-SPDIF - ROCKCHIP-SPDIF  
	                          ROCKCHIP-SPDIF  
	    root@android:/ #  
	
	
	3.tinymix 查看混响器
	    tinymix 使用方法
	        a.不加任何参数 - 显示当前配置情况 
	        b.tinymix [ctrl id] [var] 不加 [var] 可以查看该 [ctrl id] 可选选项
	    
	
	        root@android:/ # tinymix  
	        Number of controls: 7  
	        ctl type    num name                                     value  
	        0   ENUM    1   Playback Path                            OFF  
	        1   ENUM    1   Capture MIC Path                         MIC OFF  
	        2   ENUM    1   Voice Call Path                          OFF  
	        3   ENUM    1   Voip Path                                OFF  
	        4   INT 2   Speaker Playback Volume                  0 0  
	        5   INT 2   Headphone Playback Volume                0 0  
	        6   ENUM    1   Modem Input Enable                       ON  
	        root@android:/ #  
	
	对应解释:
	        英文                              中文                          备注
	
	        Playback Path                     音频输出通道
	        Capture MIC Path                  音频输入通道
	        Voice Call Pah                    通话音频通道                  设备没有通话模块,暂无法测试
	        Voip Pah                          IP 电话音频通道               场景 Gtalk;值有: SPK/HP_NO_MIC/BT
	        Speaker Playback Volume           扬声器音量                    和上层音量值无关
	        Headphone Playback Volume         耳机音量                      同上
	        Modem Input Enable                暂不知何用                    经测试不能控制音频输入输出
	
	    Playback Path有:
	
	        英文                              中文                          备注
	
	        OFF                               关闭
	        RCV                               -
	        SPK                               扬声器                        常用 
	        HP                                耳机带麦
	        HP_NO_MIC                         耳机无麦                      常用
	        BT                                蓝牙
	        SPK_HP                            -
	        RING_SPK                          -
	        RING_HP                           -
	        RING_HP_NO_MIC                    -
	        RING_SPK_HP                       -
	
	    例:将输出切换到扬声器
	
	        root@Android:/ # tinymix 0 SPK

Android

这里只是简单根据各本书上的流程进行了相关高通 8.0 代码的追溯,比较多也比较乱,大概主要就追了 Audio 服务启动初始化,
以及从 App -> Linux 放音这两线,其他的只是针对书本上的流程进行了总结

整体框架图

Linux/Android 音频驱动从概念到 APP_第16张图片

相关类及概念流程介绍

	 Audio 系统是 Android 平台的重要组成部分,它主要包括三方面内容:
			AudioRcorder 和 AudioTrack: 这两个类属于 Audio 系统对外提供的 API 类,通过 
	            它们可以完成 Android 平台上音频数据的采集和输出任务。
	        
	        AudioFlinger:它是 Audio 系统的工作引擎,管理着系统中的输入输出音频流,并承担
	            音频数据的混音,以及读写 Audio 硬件等工作以实现数据的输入输出功能。
	            
	        AudioPolicyService:它是 Audio 系统的策略控制中心,具体掌管系统中声音设备的选择 
	            和切换、音量控制等功能。
	            
	            AudtioPolicyService 用于路由 AudioTrack 到 PlaybackThread 中,即对应的硬件
	            
	            AudioPolicyService 通过 AudioPolicyManager 来与 AF/AS 交互的
	    
	    
	 	AudioTrack 与 AudioFlinge 是通过 IAudioTrack 交互的,在 AudioFlinge 对应的即是 TrackHandle 

相关小类介绍

------------- Java ------------------------------------
    AudioSystem: Audio 的管理 
    AudioTrack: Audio 的 PCM 输出 
    AudioRecod: Audio 录制输入
    AudioEffect: Audio 音效 
    AudioPolicy: 音频设备策略 
    Visualizer: Audio 可视化效果(Visualizer)

------------- C++ --------------------------------------
    AudioRcorder/AudioTrack: 这两个类属于 Audio 系统对外提供的 API 类,通过
                他们可以完成 Android 平台上的音频数据的采集和输出任务。
                
    AudioSystem/AudioService: 封装的 AudioFlinger/AudioPolicyService 接口,提供给 AudioTrack 

    
    AudioFlinger: 它是 Audio 系统的工作引擎,管理着系统中的输入输出音频流,并
                承担音频数据的混音,以及读写 Audio 硬件等工作以实现数据的输入/
                输出功能。
    AudioPolicyService: 它是 Audio 系统的策略控制中心,具体掌管系统中声音设备的
                选择和切换、音量控制等功能。

    AudioMixer: 混音核心类
            注:此类用于将 AudioTrack 中的 32 路 track 数据,有选择的放到消费都缓冲区中?
            AudioMixer 内部有一个 mState 成员变量
            最多达 32 路的 Track 数据就存储在其中 state_t::tracks[MAX_NUM_TRACKS] 数组中
            每个 PlaybackThread::TrackBase 在 AudioMixer 中对应 tracks 数组中的一个元素
            AudioMixer 用于混音操作的缓冲区对象和 AudioTrack/AudioFlinger 中的数据区是一个
    
    
AudioTrack 与 AudioSystem / AudioService 关系:
    AudioTrack 与底层服务间又提供了 AudioSystem 和 AudioService
    通过这两个类来访问 AudioFlinger
    前者同时提供了 Java 和 Native 两层的接口实现, 而 AudioService 则只有 Native 层的实现

    这样就降低了使用者(AudioTrack)与底层服务(AudioPolicyService, AudioFlinger 等)间的耦合
    
    只要 AudioSytem 和 AudioService 向上接口不变, 那么 AudioTrack 就不需要做任何修改
    
    AudioTrack 通过 AudioSystem 来访问 AudioPolicyService	

Linux/Android 音频驱动从概念到 APP_第17张图片

AudioFlinger

Linux/Android 音频驱动从概念到 APP_第18张图片
Linux/Android 音频驱动从概念到 APP_第19张图片
Linux/Android 音频驱动从概念到 APP_第20张图片
Linux/Android 音频驱动从概念到 APP_第21张图片

AudioPolicyService

Linux/Android 音频驱动从概念到 APP_第22张图片
Linux/Android 音频驱动从概念到 APP_第23张图片
Linux/Android 音频驱动从概念到 APP_第24张图片
Linux/Android 音频驱动从概念到 APP_第25张图片

Audio 管理

Linux/Android 音频驱动从概念到 APP_第26张图片
Linux/Android 音频驱动从概念到 APP_第27张图片

Audio 播放

Linux/Android 音频驱动从概念到 APP_第28张图片
Linux/Android 音频驱动从概念到 APP_第29张图片
Linux/Android 音频驱动从概念到 APP_第30张图片
开始播放流程细分
Linux/Android 音频驱动从概念到 APP_第31张图片
Linux/Android 音频驱动从概念到 APP_第32张图片

停止播放流程细分
Linux/Android 音频驱动从概念到 APP_第33张图片

AudioTrack 播放流程涉及类关系

    //
    //      AudioTrack ==> 
    //                  AudioSystem ==>: 是一个接口类,降低与 AudioPolicy 之间的耦合
    //                              AudioPolicyService ==>
    //                                                 AudioPolicyManager ==> 相关音频逻辑关系实现类 
    //                                                                    AudioFlinge ==> 
    //                                                                               MixerThread ==> 混音进程: 持有硬件接口
    //                                                                                          AudioMixer: 混音器 
    //                                                                                          AudioStreamOut ==> HAL 硬件代表 
    //                                                                                                         HAL ==> 
    //                                                                                                             Kernel 
    //
    //
    # 调用经过模块原因解释:
            在上面的创建 AudioTrack 流程中,经过 AS--APS--厂家实现策略
            AudioSystem 想找到 AF 中的一个工作线程,会经过 AP 返回
            原因是因为 Audio 系统需要:
                根据流类型找到对应的路由策略
                根据该策略找到合适的输出设备(指扬声器、听筒之类的)
                根据设备选择 AF 中合适的工作线程
                    如蓝牙的 MixerThread,还是 DSP 的 MixerThread
                    或者是 DuplicatingThread
            AT 根据得到的工作线程索引号,最终将在对应的工作线程中创建 Track
            之后,AT 的数据将由该线程负责处理,因为
            只有 MixerThread 与硬件设备输出相关
            
     # 从目的反推开始原因:AudioTrack:set()
        AT 的目的是把数据发送到对应的设备,如蓝牙、DSP 等
        
        代表输出设备的 HAL 对象由 MixerThread 线程执有,所以要找到对应 MixerThread
        
        AP 维护流类型和输出设备和输出设备(耳机、蓝牙耳机、听筒等)之间的关系
            不同的输出设备使用不同的混音线程
            
        AT 根据自己的流类型向 AudioSystem 查询,希望得到对应的混音线程号

Audio 播放数据流图

Linux/Android 音频驱动从概念到 APP_第34张图片
Linux/Android 音频驱动从概念到 APP_第35张图片
Linux/Android 音频驱动从概念到 APP_第36张图片
Linux/Android 音频驱动从概念到 APP_第37张图片

	> PlaybackThread 和 AudioStreamOutput    
	        PlaybackThread 类中有一个 AudioStreamOutput 类型对象
	        例如:MixerThread 有个 AudioStreamOutput 用于硬件输出
	        这个对象提供了音频数据的输出功能
	        
	        PlaybackThread 接收来自 AT 的数据,对这些数据进行混音
	        把混音的结果写到 AudioStreamOut 中,完成音频输出

相关播放线程

Linux/Android 音频驱动从概念到 APP_第38张图片
在这里插入图片描述
Linux/Android 音频驱动从概念到 APP_第39张图片

	> 工作线程介绍
	    RecordThread: 录音线程,用于音频输入
	    PlaybackThread: 回放线程,用于音频输出
	        两个 Track 数组:
	            mActiveTracks: 表示当前活跃的 Track
	            mTracks: 表示这个线程创建的所有 Track
	    
	        DirectOutputThread: 直接输出线程,选择一路音频输出
	        MixerThread: 混音线程,用于将多个源音频数据混音后输出
	            DuplicatingThread: 多路输出,也能混音
	                mOutputTracks: 表示多路输出的目的端

Linux/Android 音频驱动从概念到 APP_第40张图片

Audio 录音

Linux/Android 音频驱动从概念到 APP_第41张图片
Linux/Android 音频驱动从概念到 APP_第42张图片

Linux/Android 音频驱动从概念到 APP_第43张图片
Linux/Android 音频驱动从概念到 APP_第44张图片

Linux/Android 音频驱动从概念到 APP_第45张图片
Linux/Android 音频驱动从概念到 APP_第46张图片
Linux/Android 音频驱动从概念到 APP_第47张图片

Audio 音效

Linux/Android 音频驱动从概念到 APP_第48张图片
Linux/Android 音频驱动从概念到 APP_第49张图片

	音效类继承关系: frameworks/av/media/libeffects 
    AudioEffect
        BassBoost: 重低音 
        EnvironmentalReverb:环境音混响
        Equalizer:均衡器
        PresetReverb:预置混响
        Virtualizer:可视化 

        Virtualizer: 虚拟器

Linux/Android 音频驱动从概念到 APP_第50张图片
Linux/Android 音频驱动从概念到 APP_第51张图片

	#################################################
	# 3rd audio effect的实现   
	#################################################
	
	# audio_effect_library_t: 定义了所有effect的一个统一接口
	    // Audio_effect.h (hardware\libhardware\include\hardware)
	    //      所有的音效库必须实现一个名为 AUDIO_EFFECT_LIBRARY_INFO_SYM 的 audio_effect_library_t 的结构
	    typedef struct audio_effect_library_s {
	        // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
	        uint32_t tag;
	        // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
	        uint32_t version;
	        // Name of this library
	        const char *name;
	        // Author/owner/implementor of the library
	        const char *implementor;
	    
	        create_effect(): 就是创建对应的音效引擎,得到引擎控制接口 effect_interface_t
	        release_effect): 释放对应的音效引擎
	        get_descriptor(): 通过 uuid 去获取音效描述符
	        
	    }
	
	
	# 音效处理引擎接口 effect_interface_s
	    包括四个函数指针:      
	        process(): 音效处理
	        command(): 用于向音效引擎发送命令和接收对命令的回复
	                // Audio_effect.h (system\media\audio\include\system)
	                /
	                //      Effect control interface
	                /
	
	                //
	                //--- Standardized command codes for command() function
	                //
	                enum effect_command_e {
	                   EFFECT_CMD_INIT,                 // initialize effect engine
	                   EFFECT_CMD_SET_CONFIG,           // configure effect engine (see effect_config_t)
	                   EFFECT_CMD_RESET,                // reset effect engine
	                   EFFECT_CMD_ENABLE,               // enable effect process
	                   EFFECT_CMD_DISABLE,              // disable effect process
	                   EFFECT_CMD_SET_PARAM,            // set parameter immediately (see effect_param_t)
	                   EFFECT_CMD_SET_PARAM_DEFERRED,   // set parameter deferred
	                   EFFECT_CMD_SET_PARAM_COMMIT,     // commit previous set parameter deferred
	                   EFFECT_CMD_GET_PARAM,            // get parameter
	                   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio.h, audio_devices_t)
	                   EFFECT_CMD_SET_VOLUME,           // set volume
	                   EFFECT_CMD_SET_AUDIO_MODE,       // set the audio mode (normal, ring, ...)
	                   EFFECT_CMD_SET_CONFIG_REVERSE,   // configure effect engine reverse stream(see effect_config_t)
	                   EFFECT_CMD_SET_INPUT_DEVICE,     // set capture device (see audio.h, audio_devices_t)
	                   EFFECT_CMD_GET_CONFIG,           // read effect engine configuration
	                   EFFECT_CMD_GET_CONFIG_REVERSE,   // read configure effect engine reverse stream configuration
	                   EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature.
	                   EFFECT_CMD_GET_FEATURE_CONFIG,   // get current feature configuration
	                   EFFECT_CMD_SET_FEATURE_CONFIG,   // set current feature configuration
	                   EFFECT_CMD_SET_AUDIO_SOURCE,     // set the audio source (see audio.h, audio_source_t)
	                   EFFECT_CMD_OFFLOAD,              // set if effect thread is an offload one,
	                                                    // send the ioHandle of the effect thread
	                   EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
	                };
	        get_desriptor(): 返回音效描述符
	        process_reverse(): 提供一个反向音频流,一般用于回声消除
	
	###################        
	# 音效库实现例:
	###################
	    // EffectDownmix.c (frameworks\av\media\libeffects\downmix)
	    
	        // This is the only symbol that needs to be exported
	        __attribute__ ((visibility ("default")))
	        audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
	            .tag = AUDIO_EFFECT_LIBRARY_TAG,
	            .version = EFFECT_LIBRARY_API_VERSION,
	            .name = "Downmix Library",
	            .implementor = "The Android Open Source Project",
	            .create_effect = DownmixLib_Create,
	            .release_effect = DownmixLib_Release,
	            .get_descriptor = DownmixLib_GetDescriptor,
	            // 这个结构体在Android4.3+时有发生变化,query_num_effects()和query_effect()被删除。
	            // 如果希望做库兼容性,需要检测EFFECT_LIBRARY_API_VERSION,当其为
	            //      #ifEFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION) > 2 时 query_num_effects() 和 query_effect() 不复存在
	        };
	        
	        // effect_handle_t interface implementation for downmix effect
	        // 音效处理引擎接口 effect_interface_s
	        const struct effect_interface_s gDownmixInterface = {
	                Downmix_Process,
	                Downmix_Command,
	                Downmix_GetDescriptor,
	                NULL /* no process_reverse function, no reference stream needed */
	        };
	
	    
	        # audio_buffer_t: 定义了音效输入输出的数据格式
	        # effect_param_t: 定义了音效之间、系统上下之间的通信协议(数据、格式等等)
	   
	    // audio_effects.conf 的结构如下:
	
	        libraries { // libraries 指明了库的加载路径,默认是在/system/lib/soundfx/目录下
	
	            ...
	
	            downmix {
	
	            path /system/lib/soundfx/libdownmix.so
	
	            }
	
	        }
	
	        effects {// effects 包含了该系统支持的所有音效,音效所使用的库,以及音效的 uuid
	
	          ...
	
	            downmix {
	
	                library downmix
	
	                uuid 93f04452-e4fe-41cc-91f9-e475b6d1d69f
	
	            }
	
	        }
	   
	   
	   
	# 音效引擎工厂:EffectFactory 
	    主要是为了减少 AudioFlinger 与音效引擎库的耦合
	    
	    根据配置文件中的信息装载引擎库,解析出引擎库符号(audio_effect_library_t)放在链表中,
	    再遍历链表,调用 audio_effect_library_t 的 get_descriptor 函数,进而得到音效描述符
	    effect_descriptor_t, 这样就可以得到系统中所有的音效引擎。
	        frameworks/av/media/libeffects/factory/EffectsFactory.c 
	            
	            gLibraryList: 管理音效链表
	        
	        vendor/etc/audio_effects.conf 音效配置文件
	   
	   
	# 音效使用:
	    会在 MixerThread  循环中中使用

App 音效处理流程

    # AudioEffect的具体效果作用在音频数据上,MediaPlayer只管播放音频,二者通过AudioSessionId关联起来
    #   可以说,音效的处理对 MediaPlayer 是透明的,具体的处理由 Android 框架进行
    
    # 如果要应用全局音频输出的混响效果必须指定 audioSession=0,并且要求有 MODIFY_AUDIO_SETTINGS 权限

    //创建 MediaPlayer,音频源为/res/raw/audio.mp3
    mMediaPlayer = new MediaPlayer.create(this, R.raw.beautiful);

    //创建Equalizer,通过AudioSessionId绑定到MediaPlayer
    Equalizer mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());

	//启用、获取/设置参数
    mEqualizer.setEnabled(true);
    short bands = mEqualizer.getNumberOfBands();
    mEqualizer.setBandLevel(band, level);

    //MP3 播放,创建 Equalizer
    mMediaPlayer.start();

apk 使用 AudioTrack

	// 1. 根据音频数据的特性来确定所要分配的缓冲区的最小 size 
    int bufsize = AudioTrack.getMinBufferSize(800, // 采样率:每秒 8000 个点 
                                        AudioTrack.CHANNEL_CONFIGURATION_STEREO,// 声道数:双声道
                                        AudioTrack.ENCODING_PCM_16BIT  // 采样精度:一个采样点 16 比特,相当于 2 个字节

	// 2. 创建 AudioTrack 
    //      创建 AudioTrack 对象
    //      native_setup(): 创建一个本地 AudioTrack 与 AudioFlinger 建立联系
    //      AudioTrack 与底层服务间又提供了 AudioSystem 和 AudioService
    //          前者同时提供了 Java 和 Native 两层的接口实现
    //          而 AudioService 则只有 Native 层的实现
    //          这样就降低了使用者(AudioTrack)与底层服务(AudioPolicyService, AudioFlinger 等)间的耦合
    //          【只要 AudioSytem 和 AudioService 向上接口不变】
    //          【那么 AudioTrack 就不需要做任何修改】
    //          AudioTrackThread: 用于给 AudioFlinger 发数据
    //          AudioTrack 在 AudioFlinger 内部以 Track 类来管理的
    //          AudioTrack 与 AudioFlinger 通过 IAudioTrack 通信
    //              AudioFlinger 服务名:media.audio_flinger
    //  
    //          AudioTrack::set()
    //                  // 此线程用于主动从用户那里获取数据时用
    //                  mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
    //                  mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
    //                  
    //                  // create the IAudioTrack
    //                  status_t status = createTrack_l();                            
    //                          AudioSystem::getOutputForAttr()
    //                              AudioPolicyService()::getOutputForAttr()
    //                                  AudioPolicyManager()::getOutputForAttr()
    //                                      1. getStrategyForAttr(): 获取 stream 音频类型对应的 Strategy
    //                                          每种 Stream 类型都有对应的路由策略(Routing Strategy)
    //                                          getDeviceForStrategy(): 进一步为这一 Strategy 匹配最佳的音频设备
    //                                      2. getOutputForDevice(): 应用策略,判断哪些 Output 符合用户传入的 stream 类型
    //                                          用于获得所有支持 device 设备的 output, 并添加到 outputs 中
    //                                          3. selectOutput(): 选择最适合的 Output
    //                                              1> 处理一些特殊情况 
    //                                              2> 开始择优判断,逐个处理所有 Outputs
    //                                              3> 根据优先级做出最后的选择
    //                                                  Flag 与要求相似度最高的 Output
    //                                                  Primary Output
    //                                                  如果上面两种都找不到,则默认返回第一个 Output 
    //                          
    //                          // 该函数返回 IAudioTrack(实现上是 BpAudioTrack)对象,后续 AF 和 AT 的交互就是围绕 IAudioTrack 进行的
    //                          audioFlinger->createTrack()
    //                                 ----------- Binder --------------------------
    //                                 AudioFlinger::createTrack()
    //                                     # 他会通过 AS 查询 APS 获得 AF 中的对应硬件的播放线程
    //                                     # 然后创建一个 Track 添加到此播放线程中去
    //                                     
    //                                     # 根据索引号找到一个工作线程,PlaybackThread,例如 MixerThread 线程
    //                                     # 此工作线程与 HAL 输出对象相关,在 AudioPolicyService 中创建
    //                                     # 在新的工作线程对象中创建一个 Track 对象,用于写音频到硬件
    //                                     # 然后再创建一个 TrackHandler 的 Binder 代理对象
    //                                     # 用来通过 Binder 接收请求,让新工作线程 Track 处理
    //                                     
    //                                     1> 选择工作线程:AudioFlinger::checkPlaybackThread_l()
    //                                           AF 会创建几个工作线程,AT 会找到对应的工作线程
    //                                     checkPlaybackThread_l(output); 
    //                                         AudioFlinger::openOutput(): 产生唯一的 audio_io_handle_t
    //                                             而 AudioTrack 调用 createTrack 时,需要传入这个全局标记值,从而找到匹配的 PlaybackThread
    //                                         
    //                                     2> AudioFlinger::PlaybackThread::createTrack_l()
    //                                            创建 Track 对象,添加到内部数组 mTracks 中    
    //                                     createTrack_l()    
    //                                           找到匹配的 PlaybackThread 后,在其内部创建一个 PlaybackThread::Track 对象 
    //                                     
    //                                     3> Track 创建共享内存和 TrackHandler
    //                                           Track 创建了共享内存  
    //                                           CB 对象通过 placement new 方法创建于这块共享内存中
    //                                           Track 没有基于 Binder 通信,所以不能接收远端请求
    //                                           这里用其初始化一个代理对象 TrackHandle 
    //                                           TrackHandle 能基于 Binder 通信,可接收远端通信
    //                                           并能调用 Track 相应函数进行处理,这就是代理模式
    //            
    //                                     TrackHandle():实际就是 IAudioTrack
    AudioTrack trackplayer = new AudioTrack(
                                            AudioManager.STREAM_MUSIC, // 音频流类型
                                            800,                       // 设置音频数据的采样率 32k,如果是44.1k就是44100
                                            AudioFormat.CHANNEL_CONFIGURATION_STEREO, // 
                                                    // 音频流的类型:和 Audio 系统对音频的管理策略有关
                                                        STREAM_ALARM: 警告声 
                                                        STREAM_MUSIC: 音乐声,例如 music 等 
                                                        STREAM_RING: 铃声 
                                                        STREAM_SYSTEM: 系统声音,例如低电提示音、锁屏音等 
                                                        STREAM_VOCIE_CALL: 通话声 
                                            
                                            
                                            AudioFormat.ENCODING_PCM_16BIT,// 设置音频数据块是8位还是16位,这里设置为16位。好像现在绝大多数的音频都是16位的了
                                            bufsize,
                                            AudioTrack.MODE_STREAM      // 数据加载模式,在这里设置为流类型,另外一种MODE_STATIC
                                                    // AudioTrack 数据加载模式:
                                                        #MODE_STREAM: 在这种模式下,通过 write 一次次把音频数据写到 AudioTrack 中。这和平时通过 write 系统调用
                                                                    往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的 Buffer 中拷贝到 AudioTrack 
                                                                    内部的 Buffer 中,这在一定程序上会引起延时。
                                                                    
                                                        #MODE_STATIC: 这种模式下,在 play 之前只需要将所有的数据通过一次 write 调用传递到 AudioTrack 的内部缓冲区 
                                                                    中,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。但它
                                                                    也有一个缺点,就是一次 write 的数据不能太多,否则系统无法分配足够的内存来存储全部数据。
                                                
                
    );


	// 3. 开始播放 
    //      AT 调用 IAudioTrack.start() 由于 TrackHandler 的代理作用
    //      实际上会由具体的 Track 对象进行处理
    //      Track 代表一路音频流,他需要输出到 MixerThread 中的硬件设备中
    //      Track::start():
    //          通过 AS 查询 APS 获得 AF 对应硬件的 MixerThread 线程
    //          调用其 addTrack_l() 函数
    //          AudioFlinger::PlaybackThread::addTrack_l():
    //              设置重试次数,等待数据写 write()
    //              将此 Track 添加到 MixerThread 的活跃队列 mActiveTracks
    //              广播一个事件,触发 MixerThread 线程,通知有活跃数组加入
    //
    //              ----------- MixerThread 线程 -------------------
    //              1> MixerThread 接收广播事件后( track.start() 在 apk 调用那块写了)
    //                  Thread 类线程工作都是在 threadLoop() 中完成
    //                  MixerThread::threadLoop()
    //                      首先处理通知信息或配置请求,比如向监听者通知 AF 信息
    //                      或者根据配置请求进行音量控制、声音设备切换等
    //                      
    //                      然后调用 prepareTrack_l() 检查活跃的 Tracks 是否有数据
    //                          2> prepareTrack_l() 和 process() 分析
    //                              prepareTrack_l(): 
    //                                  一个混音器可支持 32 个 Track, 依次检查活跃 Track
    //                                  对当前活跃 Track, 输入数据设置 AudioMixer 要进行混音处理
    //                                  
    //                      调用混音器对象 AudioMixer:process() 进行混音处理
    //                          process(): 
    //                              对需要根据 prepareTrack_l() 设置混音参数
    //                              对活跃 Track 输入音频数据调用 hook() 函数进行处理
    //                          3> AudioMixer 对象分析
    //                              在其构造函数中初始化 32 路 Track 和其 hook() 函数 
    //                              支持的 hook() 函数有:
    //                                  process__validate: 根据 Track 格式、数量选择其他处理函数
    //                                  process__nop: 什么也不做
    //                                  process__genericNoResampling: 普通无需重采样
    //                                  process__genericResamplinge: 普通需重采样
    //                                  process__OneTrack16BitsStereoNoResampling: 一路音频流,双声道,PCM16 格式,无需重采样
    //                                  process__TwoTrack16BitsStereoNoResampling: 两路音频流,双声道,PCM16,无重采样
    //                          4> 在 AF prepare_l() 会为每个准备好的 Track 使能混音标志
    //                                  AudioMixer->enbale() 使能混音,设置 hook() 函数为 procss_validate()          
    //                                  AudioMixer::process__validate(): 会根据 Track 情况设置合适 hook() 函数
    //                      调用音频输出对象 AudioOutputStream.write() 输出音频到设备
    trackplayer.play();

	。。。
    
    // 4. 调用 write 写数据
    //   5> 怎么消费数据
    //           数据是在 AT 中通过 ObtainBuffer() 得到缓冲区
    //           然后 memcpy 写入,最后 releaseBuffer() 释放缓冲区得到的
    //           消费数据就是在对应 hook() 中进行的,如在 
    //           ------------ 消费数据 ------------------
    //           AudioMixer::process__oneTrack16BitsStereoNoResampling():
    //               首先找到被激活的 Track,即本 hook() 绑定的 Track 对象
    //               然后通过 getNextBuffer() 获得可读数据缓冲
    //               
    //               再然后进行数据处理,即混音
    //               
    //               最后调用数据复制到 out 缓冲,得到混音后数据
    //               
    //               调用 Track 的 releaseBuffer() 释放缓冲区
    //               
    //               6> getNextBuffer 和 releaseBuffer 分析
    //                   getNextBuffer(): 从缓冲区中得到一块可读空间
    //                       即根据 CB 记录的读写位置等计算可读缓冲区位置
    //                       首先通过 frameReady() 得到可读帧数
    //                       然后根据可读帧数等信息等到可读空间首地址
    //                   releaseBuffer(): 通过 stepServer() 更新读位置    
    trackplayer.write(bytes_pkg, 0, bytes_pkg.length);  // 往 track 中写数据


	。。。
    
    // 5. 停止播放和释放资源 
    //    1> TrackHandle 和 Track 的回收
    //            来自 AT 的 stop() 请求最终会通过 TrackHandle 这个代理
    //            交给具体的 Track 的 stop 进行处理
    //            AudioFlinger::PlaybackThread::Track::stop():
    //                如果 Track 最初牌活跃数组中,则
    //                设置 Track 状态为停止 STOPPED 状态
    //                因为在 MixerThread::prepareTrack_l() 中,如果 AT 写
    //                数据快,而 AF 消耗快,则声音还是会在调用 stop() 后听到
    //                所以这里还有其他处理操作
    //                这就是 AT 端 stop 后会被很快 delete, 导致 AF 端的
    //                TrackHandle 也被 delete 
    //                所以会调用到 TrackHandle 的析构函数
    //                    这里会调用到 MixerThread->destroyTrack_l(),即 playbackThread->destroyTrack()
    //                        将不在活跃数组的 Track 从 mActiveTracks 数组移出
    //                        deleteTrackName_l(): 由 PlaybackThread 子类实现,回收一些资源
    //                TrackHandle 的 delete 会导致所代理的 Track 对象也被删除
    //                
    //                2> Client 的回收 
    //                    前面说过,凡是使用 AT/AR 的线程,都会被 AF 当作 Client 对象
    //                    Client 是 AudioFlinge 对客户端的封装, AF 的 Client,并且 Client 用它的进程 pid 为标识
    //                    Track 的析构,会导致它的基类 TrackBase 析构函数被调用
    //                    AudioFlinger::ThreadBse::TrackBase::~TrackBase()
    //                        释放 定位 new 声明的对象 
    //                        如果 mClient 强弱引用计数都为 0,则导致该 Client 被 delete
    trackplayer.stop(); // 停止播放 

杂项流程记录

	###########################################
	# 声音路由切换实例分析:邓凡平 2.2 
	###########################################  
	//    这一切主要介绍耳机插入,详细介绍下声音路由切换实例
	//    #########################
	//    #1. 耳机插拔事件的处理
	//    #########################
	//        耳机插入后,系统会发出一个广播,Java 层的 AudioService 会接收这个广播
	//        调用内部类 AudioServiceBroadcastReceiver 处理该事件
	//        
	//        //1> 耳机插拔事件的接收
	//            AudioServiceBroadcastReceiver::onReceive()
	//                判断耳机状态,是插入还是拔出,调用 
	//                AudioSystem::setDeviceConnectionState() 设置设备连接状态
	//                
	//                //2> setDeviceConnectionState: 设置设备连接状态
	//                    直接调用 jni 的函数处理
	//                    ------------ jni -------------------------
	//                    android_media_AudioSystem_setDeviceConnectState()
	//                        调用 Native 的 AudioSystem::setDeviceConnectionState()
	//                            调用 AMB 处理,AMB 是需要厂家实现的,但一般直接用 AudioPolicyManagerBase 实现
	//                            AudioPolicyManagerBase::setDeviceConnectionState()
	//                                一次只能设置一个设备
	//                                根据设备号判断是不是输出设备,耳机属于输出设备
	//                                getNewDevice():得到一个 MixerThread 对应的硬件设备
	//                                
	//                                    //3> getNewDevice()
	//                                        根据索引号找到对应的 AudioOutputDescriptor
	//                                            AudioOutputDescriptor: 用来记录并维护与输出设备 DSP 相关的信息,
	//                                            此对象在 AudioPolicyManagerBase 构造函数中创建的
	//                                            如使用该设备硬件上,他代表的是 DSP 设备,
	//                                            如 PMIC 中的 Audio Codec
	//                                            此对象是 AMB 用来控制和管理音频输出设备的
	//                                            的流个数,各个流音量该设备所支持的采样率,采样精度等
	//                                            
	//                                        当前应用场景为正在听歌,会走 getDeviceForStrategy(STRATEGY_MEDIA)    
	//                                            AudioPolicyManagerBase::getDeviceForStrategy()
	//                                                重新计算策略所对应的输出设备
	//                                                此函数会根据当前策略,以及通话、蓝牙状态,选择输出硬件设备
	//                                
	//                                updateDeviceForStrategy(): 更新各种策略使用的设备
	//                                    //4> AudioPolicyManagerBase::updateDeviceForStrategy()
	//                                        重新计算每种策略使用的设备,保存到 mDeviceForStrategy[] 中,启 cache 作用
	//                                        mdeviceForStrategy[] = getDeviceForStrategy()
	//                                
	//                                setOutputDevice(): 设置新的输出设备
	//                                    //5> AudioPolicyManagerBase::setOutputDevice()
	//                                        创建请求,需要发送到 AF 对应的工作队列中进行处理
	//                                        比如 DSP 的 MixerThread 或者蓝牙的 MixerThread
	//                                        AudioParameter param = AudioParameter()
	//                                        调用 AP 的对象进行发送处理
	//                                        
	//                                        mpClientInterface->setParameters()
	//                                            最终调用到 APS 的 setParameters()
	//                                            AudioPolicyService::setParameters()
	//                                                把这个请求加入到 AudioCommandThread 中处理
	//                                                此线程在 AudioPolicyService 构造时创建
	//                                                创建 AudioCommandThread 用于处理控制命令,例如路由切换、音量调节等
	//                                                #########################
	//                                                #2. AudioCommandThread
	//                                                #########################
	//                                                        AudioCommandThread 有一个请求处理队列
	//                                                        AP 负责往该队列提交请求,而 AudioCommandThread
	//                                                        在它的线程函数 threadLoop 中处理这些命令
	//                                                        //1> AudioCommandThread::threadLoop()
	//                                                            case STOP_TONE: TONE 处理
	//                                                            case SET_VOLUME: 设备音量
	//                                                            case SET_PARAMETERS: 处理路由设置请求
	//                                                                转到 AudioSystem 处理
	//                                                                //2> AudioSystem::setParameters()   
	//                                                                    交给 AF 处理
	//                                                                    AudioFlinger::setParameters()
	//                                                                        根据索引号找到对应的混音线程 MixerThread
	//                                                                        将请求交由混音线程处理
	//                                                                        //3> MixerThread::treadLoop()
	//                                                                            MixerThread::checkForNewParameters()
	//                                                                                路由设置需要硬件参数,直接交给代表
	//                                                                                音频输出设备 HAL 对象处理
	//                                                                                
	//                                                                                //4> HAL 对象的处理例
	//                                                                                    以高通公司为例
	//                                                                                    AudioHardware::AudioStreamOutMSM72xx:setParameters()
	//                                                                                        AudioHardware::doRouting()
	//                                                                                            AudioHardware::doAudioRouteOrMute()
	//                                                                                                硬件相关的代码
	//                                                                                                do_route_audio_dev_ctrl()
	//                                                                                                    open(/dev/msm_audio_ctl)
	//                                                                                                    ioctl(): 通过 ioctl 切换设备
	//                                    applyStreamVolumes(): 设置音量                                                
	//    
	
	###########################################
	# DuplicatingThread破解:邓凡平 2.2 
	###########################################
	//    当一份数据同时需要发给 DSP 和蓝牙 A2DP 设备时使用 DuplicatingThread
	//    
	//    1. DuplicatingThread 的来历
	//        假设已经连接上了一个蓝牙耳机,耳机中断处理?
	//        会调用到 AudioPolicyManagerBase::setDeviceConnectionState() 设置设备连接状态
	//        AudioPolicyManagerBase::setDeviceConnectionState()
	//            专门处理 A2DP 设备的连接
	//            handleA2dpConnection()
	//                先为 mA2dpOutput 创建一个蓝牙用的 MixerThread
	//                SONIFCATION 策略的音频流类型需要同时从蓝牙和 DSP 中传出
	//                代表音频流有:来电铃声、短信通知等
	//                所以要创建一个 Duplicateoutput 线程,传入参数为蓝牙 MixerThread
	//                    mpClientInterface->openDuplicateOutput()
	//                    # openDuplicateOutput() 结果示意图:
	//                        
	//                                     [DuplicatingThread 线程]
	//                                        .mOutputTracks 
	//                                       |             |
	//                                       |             |
	//                                 OutputTrack_0    OutputTrack_1
	//                                       |             |
	//                                       |             |
	//                        [蓝牙 MT 线程] V             V [DSP MT 线程]
	//                                .mTracks            .mTracks 
	//                            
	//                            
	//                            蓝牙中有一个成员为 OutputTrack()
	//                            DT 的 mOutputTracks 也有一个成员指向 OutputTrack()  
	//
	//                            【这就好像 DT 是 MT 的客户端一样】
	//                                与前面的 AT 是 AF 的客户端类似 
	//                        
	//                                    
	//                        # 3. DT 的客户端 AT  
	//                            DT 是从 MT 中派生的,根据 AP 和 AT 的交互流程可知
	//                                当 AT 创建流类型对应策略为 SONIFACATION 时
	//                                他会从 AP 中得到代表 DT 的线程索引号
	//                            
	//                            由于 DT 没有重载 createTrack_l(), 因此也会类似 MT 过程
	//                                创建一个 Track, 用于跟踪音频流
	//                                然后配合两个 OutputTrack 进程内缓冲
	//                                把来自 AT 的音频数据原封不动的发给蓝牙 MT 和 DSP MT
	//                            
	//                            
	//                            # 有 AT 的 DT 全景图 
	//                            
	//                                        [AudioTrack]
	//                                         IAudioTrack 
	//                                            /\
	//                                            ||
	//                                            \/
	//                                          mTracks
	//                                     [DuplicatingThread]
	//                                        mOutputTracks 
	//                                        |           |
	//                                 OutputTrack_0  OutputTrack_1
	//                                        |           |
	//                                     mTracks      mTracks
	//                                    [蓝牙 MT]     [DSP MT]
	//
	//                                AT 通过 Track 将音频流数据发给 DT
	//                                DT 通过 OutputTrack() 将数据分别发给蓝牙 MT 和 DSP MT
	//
	//                                    
	//                            # 4. DT 的线程函数
	//                                AF/DuplicatingThread::threadLoop()
	//                                    和 MT 处理一样,处理配置请求
	//                                    如果 AT 的 Track 停止了,则需要停止和 MT 共享的 OutputTrack
	//                                    prepareTracks_l(): DT 派生自 MT, 功能一样,检查活跃的 Tracks 是否有数据
	//                                    outputsReady(): 检查 OutputTracks 对应的 MT 状态
	//                                    调用 AudioMixer 进行混音处理
	//                                    outputTracks[]->write(): 将混音后的数据写到 outputTrack 中
	//                                        AT 调用 start() 将导致 DT 的 Track 加入到上面的活跃数组中
	//                                        蓝牙 MT 与 DSP MT 的则在 write() 将 OutputTrack 加入对应线程活跃数组
	//                                        AF/PT/OutputTrack::write()
	//                                            start(): 如果此 Track 没有活跃,则调用 start() 激活
	//                                            现在 AF 中的数据传递有三个线程:一个 DT, 两个 MT
	//                                            MT 作为二级消费者可能来不及消费数据
	//                                            所以 DT 提供了一个缓冲区进行缓冲来不及处理的数据
	//                                            最多可缓冲 10 组数据
	//                                            数据就这样通过 DT 的帮助,从 AT 
	//                                            传输到蓝牙 MT 和 DSP 的 MT 中
	//                                            缺点:数据传输比直接使用 MT 传输要缓慢
	//                            
	//                    # 最终的处理都是在 AF 中
	//                    AudioFlinger::openDuplicateOutput()
	//                        获得对应蓝牙的 MixerThread
	//                        获得对应 DSP 的 MixerThread
	//                        new DuplicatingThread(): 创建 DuplicatingThread, 传入第二个参数是 MixerThread
	//                            2. AF/DuplicatingThread::DuplicatingThread()
	//                                DT 是 MT 的派生类,所以先要完成基类的构造,它会创建一个 AudioMixer
	//                                addOutputTrack(): 然后把代表 DSP 的 MT 加入进来
	//                                AF/DuplicatingThread::addOutputTrack()
	//                                    构造一个 OutputTrack,第一个参数是 MT
	//                                        AF/PT/OutputTrack::OutputTrack():
	//                                            OutputTrack 从 Track 派生,所以先调用基类的构造,创建一块内存
	//                                            内存的结构如图 7-4 所示,前面为 CB 对象,后面为数据缓冲
	//                                            然后将这个 Track 加入到 MT 的 Track 中
	//                                            表示 DT 将往 MT 中写数据
	//                                    把这个 OutputTrack 加入到 mOutputTracks 数组保存
	//                        加入代表 DSP 的 MixerThread
	//                            DuplicatingThread->addOutputTrack()
	//                        经过上面两步,DT 分别构造了两个 OutputTrack
	//                        一个对应蓝牙的 MT,另一个对应 DSP 的 MT
	//                        
	//                然后创建一个 new AudioOutputDescriptor 对象 
	//                    AudioOutputDescriptor: 用来记录并维护与输出设备 DSP 相关的信息,
	//                    此对象在 AudioPolicyManagerBase 构造函数中创建的
	//                    如使用该设备硬件上,他代表的是 DSP 设备,
	//                    如 PMIC 中的 Audio Codec
	//                    此对象是 AMB 用来控制和管理音频输出设备的
	//                    的流个数,各个流音量该设备所支持的采样率,采样精度等
	###########################################
	# 音量控制: 林学森 4.3
	###########################################
	// 1. AudioManager
	//     当用户按下音量调节键
	//         public static KeyEvent extends InputEvent implements Parcelable
	//             1. AudioManager.java
	//                 handleKeyDown()
	//                     adjustSuggestedStreamVolume()
	//                         IAudioService service = getService()
	//                             服务名为 = audio
	//                         service.adjustSuggestedStreamVolume()
	//                         ------- AudioService.java ----------------------
	//                         adjustSuggestedStreamVolume()
	//                             getActiveStreamType(): 获得当前流类型
	//                             adjustStreamVolume()
	//                                 函数重点:
	//                                     1> 计算 oldIndex(之前的音量值),index(要调整的音量值)和 flags
	//                                     2> 调用 sendVolumeUpdate 和 sendMsg 把上一步的计算结果发送出去
	//                                 1. 为各 StreamType 寻找 Alias 归类
	//                                     获取当前streamType 对应的 alias
	//                                 2. 为 Stream Alias 寻找匹配的 device
	//                                     根据 stream alias 来查询匹配的输出设备
	//                                 3. 获取对应 device 的 index
	//                                     通过 VolumeStreamState() 获得音量值(index)
	//                                         VolumeStreamState(): 维护了一个 mIndex 数组来记录 device 对应的 index 值
	//                                 4. 调节 index
	//                                     为 UI 显示条做前期准备
	//                                 5. 音量调节对于音量模式的影响
	//                                     音量的调节还可能与手机的铃声模式(Ringer Mode)有关:
	//                                         静音模式
	//                                         震动模式
	//                                         正常模式
	//                                 6. 将音量调节事件发送给下一个处理者
	//                                     sendMsg():把命令投递到消息队列中
	//                                         再由 AudioHandler 做进一步处理
	//                                             AudioHandler 除了将音量值保存到系统设置文件中外
	//                                             还会调用 AudioPolicyService 的相关接口来真正调整音频设备音量
	//                                             ------------- AudioPolicyService.cpp -------------------
	//                                             AudioPolicyServiceService::setStreamVolumeIndex()
	//                                                 AudioPolicyManagerBase::setStreamVolumeIndex()
	//                                                     循环查找所有匹配设备
	//                                                     通过 checkAndSetVolume() 进行音量设置
	//                                                     ----------- AudioFlinger.cpp ----------------
	//                                                     AudioFlinger::setStreamVolume()
	//                                                         首先根据 output 找到它对应的 PlaybackThread
	//                                                         而后交由这个线程具体处理流的音量
	//                                                             AudioFlinger::PlaybackThread::setStreamVolume()
	//                                                                 mStreamTypes[stream].volume = value
	//                                                             ----- 会在 PlaybackThread::threadLoop() 处理 ----------
	//                                                             AudioFlinger::MixerThread::prepareTracks_l()
	//                                                                 之前 setStreamVolume() 设置的音量值在这里会被提取出来
	//                                                                 并和主音量等其他因素进行综合运算
	//                                                                 mAudioMixer->setParameter()
	//                                                                     设置音量
	//                                                         同时把新的值记录到 mStreamTypes 中
	//                                     sendVolumeUpdate():用于产生音量调节提示音并显示系统音量条
	//             2. [email protected]
	//                 对于部分重要的物理按键(比如 HOME 和音量调节键)
	//                 系统会先判断它们是否需要做预处理,即 interceptKeyBeforeQueueing
	//                     这个函数会根据当前的具体情况(是不是在通话状态,是不是在播放音乐等)
	//                     来决定需要调整的 STREAM 类型
	//                     而且最后它会调用 AudioService.adjustStreamVolume() 
	//                     接下来的处理流程就和上面 AudioManager 中的情况一致了

相关参考资料

Linux 部分参考资料:
UDA1341TS 芯片手册
wm8993 音频芯片手册
mini2440原理图
AudioCODEC基本知识及应用_百度
相关内核源码 2.6/3.4.2

韦东山一期/三期声卡相关视频及相关源码
http://blog.csdn.net/droidphone Alsa 相关博客

Android 部分参考:
高通安卓 8.0 源码

深入理解Android内核设计思想_林学森./第 13 章 应用不再同质化 – 音频系统
深入剖析 Android 系统_杨长刚/第 14 章 Audio
深入理解 Android 卷1_邓凡平/ 第7章 深入理解 Audio 系统

你可能感兴趣的:(Android,Android,Audio,Linux,Alsa,Codec)