前言

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

硬件介绍

Codec 通用结构

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

ADC 框图

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

MIC: 麦克风
ADC: 模数转换
ADC 框图
在这里插入图片描述

DAC 框图

DAC: 数模转换
DAC 框图
在这里插入图片描述

常用数字接口

  1. IIS
  2. PCM
  3. SPDIF
    常用数学接口

其他相关术语

在这里插入图片描述

Codec 实际结构

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

硬件原理图

取自 Mini2440 手册,数字接口为:IIS+L3
UDA1341TS 应用原理图

芯片手册框图

芯片为 UD1341TS
在这里插入图片描述
在这里插入图片描述

软硬件对应示例

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

Codec 硬件逻辑

取自 wm8993 芯片手册
WM9883 SPK 输出逻辑通路

Codec Linux 抽象

WM9883 SPK 输出逻辑通路

软件介绍

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

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

Linux

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

Alsa 框架

框架图

alsa 软件体系结构

设备中的文件结构

在这里插入图片描述

Linux 相关代码路径

在这里插入图片描述

标准 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: 数据传输相关函数 
                        }

针对硬件框架抽象

在这里插入图片描述

相关软件框架实现

具体可参考附件 【非常好】音频驱动框架总结.不带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 放音这两线,其他的只是针对书本上的流程进行了总结

整体框架图

在这里插入图片描述

相关类及概念流程介绍

	 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	

在这里插入图片描述

AudioFlinger

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

AudioPolicyService

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Audio 管理

在这里插入图片描述
在这里插入图片描述

Audio 播放

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
开始播放流程细分
在这里插入图片描述
在这里插入图片描述

停止播放流程细分
在这里插入图片描述

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 播放数据流图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

相关播放线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

Audio 录音

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Audio 音效

在这里插入图片描述
在这里插入图片描述

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

        Virtualizer: 虚拟器

在这里插入图片描述
在这里插入图片描述

	#################################################
	# 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. interceptKeyBeforeQueueing@PhoneWindowManager.java
	//                 对于部分重要的物理按键(比如 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 系统

Logo

更多推荐