alsa-lib如何解析asound.conf

http://blog.****.net/haohenghai/article/details/24255451

无论是在录音还是在放音,都要打开一个PCM流,具体对应的函数原型为:

  1. int snd_pcm_open(snd_pcm_t **pcmp, const char *name,  snd_pcm_stream_t stream, int mode);  

 本文以录音为例介绍一下它的流程。录音时传入name==AndroidCapture
 先解一下各个参数的函义:

  • pcmp: 是带回的pcm流
  • name是要找开的流即放音还是录音,放音对应AndroidPlayback_Speaker_normal,录音对应AndroidCapture。放音的字符串会因为当前使用设备和设备状况不一样而有所不同,不同之处在下划线后面那部分。录音则因为大多数板子成型后,都只有一个录音设备或者接口,因此录音串对应的就是AndroidCapture
  • stream对应流的类型,是放音(0)还是录音(1).
  • mode是指设备打开模式,阻塞或非阻塞。

2. 打开流程(snd_pcm_open_conf)

       首先会根据传入的name,到配置树中查找对应的结点。 snd_config_search_definition(root, "pcm", name, &pcm_conf); 此函数将在配置树中查找对应的结点,然后将查找到的结点复制一份到
pcm_conf中。 之后,系统会调用snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);进一步分析,pcm_conf即为刚才根据传入name到配置树查找到的结点。

 

  1. static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,  
  2.                  snd_config_t *pcm_root, snd_config_t *pcm_conf,  
  3.                  snd_pcm_stream_t stream, int mode)  
  4. {  
  5.     const char *str;  
  6.     char *buf = NULL, *buf1 = NULL;  
  7.     int err;  
  8.     snd_config_t *conf, *type_conf = NULL;  
  9.     snd_config_iterator_t i, next;  
  10.     const char *id;  
  11.     const char *lib = NULL, *open_name = NULL;  
  12.     int (*open_func)(snd_pcm_t **, const char *,   
  13.              snd_config_t *, snd_config_t *,   
  14.              snd_pcm_stream_t, int) = NULL;  
  15. #ifndef PIC  
  16.     extern void *snd_pcm_open_symbols(void);  
  17. #endif  
  18.     void *h = NULL;  
  19.     if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {  
  20.         char *val;  
  21.         id = NULL;  
  22.         snd_config_get_id(pcm_conf, &id);  
  23.         val = NULL;  
  24.         snd_config_get_ascii(pcm_conf, &val);  
  25.         SNDERR("Invalid type for PCM %s%sdefinition (id: %s, value: %s)", name ? name : "", name ? " " : "", id, val);  
  26.         free(val);  
  27.         return -EINVAL;  
  28.     }  
  29.     err = snd_config_search(pcm_conf, "type", &conf);  
  30.     if (err < 0) {  
  31.         SNDERR("type is not defined");  
  32.         return err;  
  33.     }  
  34.     err = snd_config_get_id(conf, &id);  
  35.     if (err < 0) {  
  36.         SNDERR("unable to get id");  
  37.         return err;  
  38.     }  
  39.     err = snd_config_get_string(conf, &str);  
  40.     if (err < 0) {  
  41.         SNDERR("Invalid type for %s", id);  
  42.         return err;  
  43.     }  
  44.     err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);  
  45.     if (err >= 0) {  
  46.         if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {  
  47.             SNDERR("Invalid type for PCM type %s definition", str);  
  48.             goto _err;  
  49.         }  
  50.         snd_config_for_each(i, next, type_conf) {  
  51.             snd_config_t *n = snd_config_iterator_entry(i);  
  52.             const char *id;  
  53.             if (snd_config_get_id(n, &id) < 0)  
  54.                 continue;  
  55.             if (strcmp(id, "comment") == 0)  
  56.                 continue;  
  57.             if (strcmp(id, "lib") == 0) {  
  58.                 err = snd_config_get_string(n, &lib);  
  59.                 if (err < 0) {  
  60.                     SNDERR("Invalid type for %s", id);  
  61.                     goto _err;  
  62.                 }  
  63.                 continue;  
  64.             }  
  65.             if (strcmp(id, "open") == 0) {  
  66.                 err = snd_config_get_string(n, &open_name);  
  67.                 if (err < 0) {  
  68.                     SNDERR("Invalid type for %s", id);  
  69.                     goto _err;  
  70.                 }  
  71.                 continue;  
  72.             }  
  73.             SNDERR("Unknown field %s", id);  
  74.             err = -EINVAL;  
  75.             goto _err;  
  76.         }  
  77.     }  
  78.     if (!open_name) {  
  79.         buf = malloc(strlen(str) + 32);  
  80.         if (buf == NULL) {  
  81.             err = -ENOMEM;  
  82.             goto _err;  
  83.         }  
  84.         open_name = buf;  
  85.         sprintf(buf, "_snd_pcm_%s_open", str); //生成真正的open函数  
  86.     }  
  87.     if (!lib) {  
  88.         const char *const *build_in = build_in_pcms;  
  89.         while (*build_in) {  
  90.             if (!strcmp(*build_in, str))  
  91.                 break;  
  92.             build_in++;  
  93.         }  
  94.         if (*build_in == NULL) {  
  95.             buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);  
  96.             if (buf1 == NULL) {  
  97.                 err = -ENOMEM;  
  98.                 goto _err;  
  99.             }  
  100.             lib = buf1;  
  101.             sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str);  
  102.         }  
  103.     }  
  104. #ifndef PIC  
  105.     snd_pcm_open_symbols(); /* this call is for static linking only */  
  106. #endif  
  107.     open_func = snd_dlobj_cache_lookup(open_name);  
  108.     if (open_func) {  
  109.         err = 0;  
  110.         goto _err;  
  111.     }  
  112.     h = snd_dlopen(lib, RTLD_NOW);  
  113.     if (h)  
  114.         open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));  
  115.     err = 0;  
  116.     if (!h) {  
  117.         SNDERR("Cannot open shared library %s",  
  118.                lib ? lib : "[builtin]");  
  119.         err = -ENOENT;  
  120.     } else if (!open_func) {  
  121.         SNDERR("symbol %s is not defined inside %s", open_name,  
  122.                lib ? lib : "[builtin]");  
  123.         snd_dlclose(h);  
  124.         err = -ENXIO;  
  125.     }  
  126.        _err:  
  127.     if (err >= 0) {  
  128.         err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);  
  129.         if (err >= 0) {  
  130.             if (h /*&& (mode & SND_PCM_KEEP_ALIVE)*/) {  
  131.                 snd_dlobj_cache_add(open_name, h, open_func);  
  132.                 h = NULL;  
  133.             }  
  134.             (*pcmp)->dl_handle = h;  
  135.             err = 0;  
  136.         } else {  
  137.             if (h)  
  138.                 snd_dlclose(h);  
  139.         }  
  140.     }  
  141.     if (type_conf)  
  142.         snd_config_delete(type_conf);  
  143.     free(buf);  
  144.     free(buf1);  
  145.     return err;  
  146. }  
  • snd_pcm_open_conf函数的主要功能:

        分析传递过来的配置树pcm_conf的type子结点对应的字符串:hooks,然后调用由type值(hooks)构成的字符串函数(_snd_pcm_hooks_open)的过程。在我们系统中后面还要调用此函数,而且在查找lib名时,都能在build_in_pcms中找到,所以对我们来说这个函数的主要功能就是查找pcm_conf子结点type对应的字符串<如plug>构成的函数。


       snd_pcm_open_conf会根据配置树的情况,获取子节点中名为type的结点(它是一个叶子结点),并获取它的值hooks,使用str变量存储。然后会在root对应配置树中查找名为pcm_type hooks的结点。查找成功后,会在查找到的结点中搜索所索要的lib,open_name字符串。(后面要打开)。 在本系统中没有找到pcm_type hooks的结点。那么lib和open_name均为空,下面就要对lib/open_name初始化,(如果找到pcm_type empyt结点则会用该结点的孩子结点初始化)。首先对lib初始化,首先查找str是否包含在:

  1. static const char *const build_in_pcms[] = {  
  2.     "adpcm""alaw""copy""dmix""file""hooks""hw""ladspa""lfloat",  
  3.     "linear""meter""mulaw""multi""null""empty""plug""rate""route""share",  
  4.     "shm""dsnoop""dshare""asym""iec958""softvol""mmap_emul",  
  5.     NULL  
  6. };  

     这个数组中,如果在,就将lib设为null.如果不在,设置lib为默认库
 sprintf(lib, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str); 即/usr/lib/alsa-lib/libasound_module_pcm_hooks.so;如果查找发现hooks在数组中,所以lib为null。下面对open_name初始化sprintf(open_name, "_snd_pcm_%s_open", open_name);即_snd_pcm_hooks_open函数。


为了搞清楚配置选项在代码中的表现形式,可用如下码查看,此函数在conf.c中定义:

  1. int  show_snd_config(snd_config_t *config)  
  2. {   
  3.     assert(config);  
  4.     switch (config->type) {  
  5.     case SND_CONFIG_TYPE_COMPOUND:  
  6.         {  
  7.             int err;  
  8.             struct list_head *i;  
  9.             i = config->u.compound.fields.next;  
  10.             SNDERR("Start Compound---> node id =%s\n",config->id?config->id:"null");  
  11.               
  12.             while (i != &config->u.compound.fields) {  
  13.                 struct list_head *nexti = i->next;  
  14.                 snd_config_t *leaf = snd_config_iterator_entry(i);  
  15.                   
  16.                 err = show_snd_config(leaf);  
  17.                 if (err < 0)  
  18.                     return err;  
  19.                   
  20.                 i = nexti;  
  21.             }  
  22.             SNDERR("End Compound---> ---> node id =%s\n",config->id?config->id:"null");  
  23.             break;  
  24.         }  
  25.     case SND_CONFIG_TYPE_STRING:  
  26.         SNDERR("SND_CONFIG_TYPE_STRING---> leaf id=%s,str=%s\n",config->id?config->id:"null",config->u.string?config->u.string:"null");  
  27.         break;  
  28.       
  29.     case SND_CONFIG_TYPE_INTEGER:  
  30.         SNDERR("SND_CONFIG_TYPE_ INTGER --->leaf id=%s,num=%ld\n",config->id?config->id:"null",config->u.integer);   
  31.         break;  
  32.       
  33.     case SND_CONFIG_TYPE_REAL:  
  34.         SNDERR("SND_CONFIG_TYPE_ REAL--->leaf id=%s,num=%f\n",config->id?config->id:"null",config->u.real);    
  35.         break;  
  36.       
  37.     case   SND_CONFIG_TYPE_INTEGER64:  
  38.         SNDERR("SND_CONFIG_TYPE_INTEGER64--->leaf id=%s,num=%ld\n",config->id?config->id:"null",config->u.integer64);   
  39.         break;  
  40.       
  41.     case SND_CONFIG_TYPE_POINTER:  
  42.         SNDERR("SND_CONFIG_TYPE_POINTER--->leaf id=%s,num=%p\n",config->id?config->id:"null",config->u.ptr);   
  43.         break;  
  44.       
  45.     default:  
  46.         break;  
  47.     }  
  48.     return 0;  
  49. }  

    对于音频播放,其在asound.conf的配置项如下:

  1. pcm.AndroidPlayback_Speaker_normal {  
  2.     type plug  
  3.     slave.pcm {  
  4.         type hw  
  5.         card 0  
  6.         device 0  
  7.     }  
  8. }  

对于此节点,使用show_snd_config结果如下:

  1. pcm/pcm.c:2280:(snd_pcm_open) ***2280:start:name:AndroidPlayback_Speaker_normal  
  2. pcm/pcm.c:2235:(snd_pcm_open_noupdate) ***Begin:2235:name=AndroidPlayback_Speaker_normal, stream type:0  
  3. pcm/pcm.c:2249:(snd_pcm_open_noupdate) 2222222 pcm_conf  
  4. conf.c:2504:(show_snd_config) Start Compound---> node id =AndroidPlayback_Speaker_normal  
  5. conf.c:2520:(show_snd_config) SND_CONFIG_TYPE_STRING---> leaf id=type,str=plug  
  6. conf.c:2504:(show_snd_config) Start Compound---> node id =slave  
  7. conf.c:2504:(show_snd_config) Start Compound---> node id =pcm            
  8. conf.c:2520:(show_snd_config) SND_CONFIG_TYPE_STRING---> leaf id=type,str=hw  
  9. conf.c:2524:(show_snd_config) SND_CONFIG_TYPE_ INTGER --->leaf id=card,num=0  
  10. conf.c:2524:(show_snd_config) SND_CONFIG_TYPE_ INTGER --->leaf id=device,num=0  
  11. conf.c:2516:(show_snd_config) End Compound---> ---> node id =pcm        
  12. conf.c:2516:(show_snd_config) End Compound---> ---> node id =slave       
  13. conf.c:2516:(show_snd_config) End Compound---> ---> node id =AndroidPlayback_Speaker_normal  
  14. pcm/pcm.c:2260:(snd_pcm_open_noupdate) ***2260:name=AndroidPlayback_Speaker_normal,hop=0  
  15. pcm/pcm.c:2079:(snd_pcm_open_conf) ***Begin: 2079                  
  16. pcm/pcm.c:2100:(snd_pcm_open_conf) ***2100:id=type  
  17. pcm/pcm.c:2107:(snd_pcm_open_conf) ***2107:str=plug  
  18. pcm/pcm.c:2152:(snd_pcm_open_conf) ***2152:buf=_snd_pcm_plug_open  
  19. pcm/pcm.c:2156:(snd_pcm_open_conf) ***2156:str=plug                     
  20. pcm/pcm.c:2177:(snd_pcm_open_conf) ***2177:open_name=_snd_pcm_plug_open  
  21. pcm/pcm.c:2199:(snd_pcm_open_conf) ***2199:cal open_func<_snd_pcm_plug_open>  
  22. pcm/pcm_plug.c:1227:(_snd_pcm_plug_open) ****Begin:1227  
  23. pcm/pcm.c:2079:(snd_pcm_open_conf) ***Begin: 2079  
  24. pcm/pcm.c:2100:(snd_pcm_open_conf) ***2100:id=type  
  25. pcm/pcm.c:2107:(snd_pcm_open_conf) ***2107:str=hw  
  26. pcm/pcm.c:2152:(snd_pcm_open_conf) ***2152:buf=_snd_pcm_hw_open  
  27. pcm/pcm.c:2156:(snd_pcm_open_conf) ***2156:str=hw                     
  28. pcm/pcm.c:2177:(snd_pcm_open_conf) ***2177:open_name=_snd_pcm_hw_open  
  29. pcm/pcm.c:2199:(snd_pcm_open_conf) ***2199:call open_func<_snd_pcm_hw_open>  
  30. pcm/pcm_hw.c:1397:(_snd_pcm_hw_open) ***Begin:1397                     
  31. pcm/pcm_hw.c:1272:(snd_pcm_hw_open) ***Begin:1272:err=0                  
  32. pcm/pcm_hw.c:1273:(snd_pcm_hw_open) ***1273:name=(null),card=0,device=0,subdevice=-1  
  33. control/control_hw.c:375:(snd_ctl_hw_open) ***375:card:0  
  34. pcm/pcm_hw.c:1129:(snd1_pcm_hw_open_fd) ***Begin:1129:ret=0              
  35. pcm/pcm_hw.c:1237:(snd1_pcm_hw_open_fd) ***End:1237:ret=0                
  36. pcm/pcm_hw.c:1327:(snd_pcm_hw_open) ***End:1327:ret=0  
  37. pcm/pcm_hw.c:1519:(_snd_pcm_hw_open) ***Begin:1519:err=0  
  38. pcm/pcm.c:2218:(snd_pcm_open_conf) ***END: 2218:err=0  
  39. pcm/pcm_plug.c:1328:(_snd_pcm_plug_open) ****End: 1328: err=0  
  40. pcm/pcm.c:2218:(snd_pcm_open_conf) ***END: 2218:err=0  
  41. pcm/pcm.c:2264:(snd_pcm_open_noupdate) ***End:2264:err=0  

     所以与此节点对应的open函数为_snd_pcm_plug_open,通过函数sprintf(buf, "_snd_pcm_%s_open", str);生成。

 3. _snd_pcm_hooks_open <如_snd_pcm_plug_open>

 

  1. /** 
  2.  * \brief Creates a new Plug PCM 
  3.  * \param pcmp Returns created PCM handle 
  4.  * \param name Name of PCM 
  5.  * \param root Root configuration node 
  6.  * \param conf Configuration node with Plug PCM description 
  7.  * \param stream Stream type 
  8.  * \param mode Stream mode 
  9.  * \retval zero on success otherwise a negative error code 
  10.  * \warning Using of this function might be dangerous in the sense 
  11.  *          of compatibility reasons. The prototype might be freely 
  12.  *          changed in future. 
  13.  */  
  14. int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,  
  15.                snd_config_t *root, snd_config_t *conf,   
  16.                snd_pcm_stream_t stream, int mode)  
  17. {  
  18.     snd_config_iterator_t i, next;  
  19.     int err;  
  20.     snd_pcm_t *spcm;  
  21.     snd_config_t *slave = NULL, *sconf;  
  22.     snd_config_t *tt = NULL;  
  23.     enum snd_pcm_plug_route_policy route_policy = PLUG_ROUTE_POLICY_DEFAULT;  
  24.     snd_pcm_route_ttable_entry_t *ttable = NULL;  
  25.     unsigned int csize, ssize;  
  26.     unsigned int cused, sused;  
  27.     snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;  
  28.     int schannels = -1, srate = -1;  
  29.     const snd_config_t *rate_converter = NULL;  
  30.   
  31.     SNDERR("****Begin:%d\n",__LINE__);  
  32.     snd_config_for_each(i, next, conf) {  
  33.         snd_config_t *n = snd_config_iterator_entry(i);  
  34.         const char *id;  
  35.         if (snd_config_get_id(n, &id) < 0)  
  36.             continue;  
  37.         if (snd_pcm_conf_generic_id(id))  
  38.             continue;  
  39.         if (strcmp(id, "slave") == 0) {  
  40.             slave = n;  
  41.             continue;  
  42.         }  
  43. #ifdef BUILD_PCM_PLUGIN_ROUTE  
  44.         if (strcmp(id, "ttable") == 0) {  
  45.             route_policy = PLUG_ROUTE_POLICY_NONE;  
  46.             if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {  
  47.                 SNDERR("Invalid type for %s", id);  
  48.                 return -EINVAL;  
  49.             }  
  50.             tt = n;  
  51.             continue;  
  52.         }  
  53.         if (strcmp(id, "route_policy") == 0) {  
  54.             const char *str;  
  55.             if ((err = snd_config_get_string(n, &str)) < 0) {  
  56.                 SNDERR("Invalid type for %s", id);  
  57.                 return -EINVAL;  
  58.             }  
  59.             if (tt != NULL)  
  60.                 SNDERR("Table is defined, route policy is ignored");  
  61.             if (!strcmp(str, "default"))  
  62.                 route_policy = PLUG_ROUTE_POLICY_DEFAULT;  
  63.             else if (!strcmp(str, "average"))  
  64.                 route_policy = PLUG_ROUTE_POLICY_AVERAGE;  
  65.             else if (!strcmp(str, "copy"))  
  66.                 route_policy = PLUG_ROUTE_POLICY_COPY;  
  67.             else if (!strcmp(str, "duplicate"))  
  68.                 route_policy = PLUG_ROUTE_POLICY_DUP;  
  69.             continue;  
  70.         }  
  71. #endif  
  72. #ifdef BUILD_PCM_PLUGIN_RATE  
  73.         if (strcmp(id, "rate_converter") == 0) {  
  74.             rate_converter = n;  
  75.             continue;  
  76.         }  
  77. #endif  
  78.         SNDERR("Unknown field %s", id);  
  79.         return -EINVAL;  
  80.     }  
  81.     if (!slave) {  
  82.         SNDERR("slave is not defined");  
  83.         return -EINVAL;  
  84.     }  
  85.     err = snd_pcm_slave_conf(root, slave, &sconf, 3,  
  86.                  SND_PCM_HW_PARAM_FORMAT, SCONF_UNCHANGED, &sformat,  
  87.                  SND_PCM_HW_PARAM_CHANNELS, SCONF_UNCHANGED, &schannels,  
  88.                  SND_PCM_HW_PARAM_RATE, SCONF_UNCHANGED, &srate);  
  89.     if (err < 0){  
  90.         SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  91.         return err;  
  92.     }  
  93. #ifdef BUILD_PCM_PLUGIN_ROUTE  
  94.     if (tt) {  
  95.         err = snd_pcm_route_determine_ttable(tt, &csize, &ssize);  
  96.         if (err < 0) {  
  97.             snd_config_delete(sconf);  
  98.             SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  99.             return err;  
  100.         }  
  101.         ttable = malloc(csize * ssize * sizeof(*ttable));  
  102.         if (ttable == NULL) {  
  103.             snd_config_delete(sconf);  
  104.             SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  105.             return err;  
  106.         }  
  107.         err = snd_pcm_route_load_ttable(tt, ttable, csize, ssize, &cused, &sused, -1);  
  108.         if (err < 0) {  
  109.             snd_config_delete(sconf);  
  110.             SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  111.             return err;  
  112.         }  
  113.     }  
  114. #endif  
  115.       
  116. #ifdef BUILD_PCM_PLUGIN_RATE  
  117.     if (! rate_converter)  
  118.         rate_converter = snd_pcm_rate_get_default_converter(root);  
  119. #endif  
  120.   
  121.     err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);  
  122.     snd_config_delete(sconf);  
  123.     if (err < 0){  
  124.         SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  125.         return err;  
  126.     }  
  127.     err = snd_pcm_plug_open(pcmp, name, sformat, schannels, srate, rate_converter,  
  128.                 route_policy, ttable, ssize, cused, sused, spcm, 1);  
  129.     if (err < 0)  
  130.         snd_pcm_close(spcm);  
  131.       
  132.     SNDERR("****End: %d: err=%d\n",__LINE__,err);  
  133.     return err;  
  134. }  

这个函数主要完成三件事:

    1)在传入的配置树中查找子孩子ID为slave的结点

    2)然后调用snd_pcm_open_slave和snd_pcm_hooks_open

    3)最后再分析配置树是否有hooks结点,有的话调用snd_pcm_hook_add_conf。

    为了方便大家理解,还是先看一下传入_snd_pcm_hooks_open配置树的情况, 先来看一下snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);函数,在这里sconf即为slave结点中ID为pcm对应的配置树。root对应的是总的配置树(系统启动时会根据配置文件asound.conf/alsa-lib.conf加载构建)。

      snd_pcm_open_slave-->

         snd_pcm_open_named_slave-->

                 snd_pcm_open_noupdate

      由此可见snd_pcm_open_slave仍然会走到snd_pcm_open_noupdate这个函数处。然后会根据传入的参数name=default,在总的配置树中查找pcm default结点。snd_config_search_definition最终根据pcm default串查找对应的树。之后会调用 snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode)前面已经详细介过本函数了,本次调用只是传递过来的配置树不一样。

       snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode)根据当前传入树pcm_conf的情况会调用如下函数_snd_pcm_empty_open。这个函数会从当前传入树取下slave孩子结点的pcm结点对应的配置树做为根结点,并调用 snd_pcm_open_named_slave.
      snd_pcm_open_named_slave-->

          snd_pcm_open_conf

      可以看出又回到了snd_pcm_open_conf函数,只是传入的树不一样。snd_pcm_open_conf会调用_snd_pcm_plug_open.
     函数_snd_pcm_plug_open会调 用snd_pcm_open_slave及snd_pcm_plug_open。
     在传入树中有这样的子树,调用

      snd_pcm_open_slave-->

          nd_pcm_open_named_slave-->

               snd_pcm_open_conf-->

                  _snd_pcm_hw_open.

4. _snd_pcm_hw_open

     _snd_pcm_hw_open-->snd_pcm_hw_open,此函数中会根据树中的card号码,调用snd_ctl_hw_open打开/dev/snd/ControlC0,然后再打开/dev/snd/pcmC0D0c,完成后调用snd_ctl_close关闭/dev/snd/ControlC0.并调用snd_pcm_hw_open_fd,传入fd为打开的/dev/snd/pcmC0D0c.该函数完成PCM流的创建,首先创建pcm的私有数据hw (snd_pcm_hw_t结构),然后用参数(参数获取是通过ioctl命令发送到fd,然后内核将请求信息带回最后赋值给hw的成员中)初始化它,接着创建pcm(_snd_pcm结构),并将hw赋值给pcm->private_data成员。最后再调用snd_pcm_hw_mmap_status/snd_pcm_hw_mmap_control两个函数完成映射。需要注意在这里pcm有一个类型,本处创建的pcm类型为SND_PCM_TYPE_HW.
 对pcm的初始化代码如下:

  1. //创建类型为SND_PCM_TYPE_HW的pcm流,并完成一些基本初始化。  
  2.  ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);  
  3.  if (ret < 0) {  
  4.   free(hw);  
  5.   close(fd);  
  6.   return ret;  
  7.  }  
  8.  //初始化ops函数集  
  9.  pcm->ops = &snd_pcm_hw_ops;  
  10.  //初始化fast_ops函数集  
  11.  pcm->fast_ops = &snd_pcm_hw_fast_ops;  
  12.  pcm->private_data = hw;//如上所述私有数据赋值  
  13.  pcm->poll_fd = fd;/dev/snd/pcmC0D0c  
  14.  pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;  
  15.  pcm->monotonic = monotonic;  



alsa-lib如何解析asound.conf