程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> alsa音頻架構1

alsa音頻架構1

編輯:C++入門知識

 第一部分 alsa子系統關鍵結構體對象等

1.聲卡設備類型定義

[cpp]
#define SNDRV_DEV_TOPLEVEL  ((__force snd_device_type_t) 0)  
#define SNDRV_DEV_CONTROL   ((__force snd_device_type_t) 1)   
#define SNDRV_DEV_LOWLEVEL_PRE  ((__force snd_device_type_t) 2)  
#define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)  
#define SNDRV_DEV_PCM       ((__force snd_device_type_t) 0x1001)  
#define SNDRV_DEV_RAWMIDI   ((__force snd_device_type_t) 0x1002)  
#define SNDRV_DEV_TIMER     ((__force snd_device_type_t) 0x1003)  
#define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004)  
#define SNDRV_DEV_HWDEP     ((__force snd_device_type_t) 0x1005)  
#define SNDRV_DEV_INFO      ((__force snd_device_type_t) 0x1006)  
#define SNDRV_DEV_BUS       ((__force snd_device_type_t) 0x1007)  
#define SNDRV_DEV_CODEC     ((__force snd_device_type_t) 0x1008)  
#define SNDRV_DEV_JACK      ((__force snd_device_type_t) 0x1009)  
#define SNDRV_DEV_LOWLEVEL  ((__force snd_device_type_t) 0x2000) 

#define SNDRV_DEV_TOPLEVEL ((__force snd_device_type_t) 0)
#define SNDRV_DEV_CONTROL ((__force snd_device_type_t) 1) 
#define SNDRV_DEV_LOWLEVEL_PRE ((__force snd_device_type_t) 2)
#define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)
#define SNDRV_DEV_PCM  ((__force snd_device_type_t) 0x1001)
#define SNDRV_DEV_RAWMIDI ((__force snd_device_type_t) 0x1002)
#define SNDRV_DEV_TIMER  ((__force snd_device_type_t) 0x1003)
#define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004)
#define SNDRV_DEV_HWDEP  ((__force snd_device_type_t) 0x1005)
#define SNDRV_DEV_INFO  ((__force snd_device_type_t) 0x1006)
#define SNDRV_DEV_BUS  ((__force snd_device_type_t) 0x1007)
#define SNDRV_DEV_CODEC  ((__force snd_device_type_t) 0x1008)
#define SNDRV_DEV_JACK      ((__force snd_device_type_t) 0x1009)
#define SNDRV_DEV_LOWLEVEL ((__force snd_device_type_t) 0x2000)一個聲卡可以有多個聲卡設備,alsa中用snd_card描述聲卡對象,用snd_device描述聲卡設備對象

2.聲卡結構體

[cpp]
struct snd_card { 
    int number;             //聲卡索引號  
    char id[16];            //id識別字串  
    char driver[16];        //驅動名  
    char shortname[32];     //短名  
    char longname[80];      //長名  
    char mixername[80];     /* mixer name */ 
    char components[128];   /* card components delimited with space */ 
    struct module *module;  //模塊所有者  
    void *private_data;     /* private data for soundcard */ 
    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;     //設備文件  
    struct device *card_dev;    //聲卡設備文件  
#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  
}; 

struct snd_card {
 int number;    //聲卡索引號
 char id[16];   //id識別字串
 char driver[16];  //驅動名
 char shortname[32];  //短名
 char longname[80];  //長名
 char mixername[80];  /* mixer name */
 char components[128]; /* card components delimited with space */
 struct module *module; //模塊所有者
 void *private_data;  /* private data for soundcard */
 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;  //設備文件
 struct device *card_dev; //聲卡設備文件
#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.1.全局變量snd_cards

在"/sound/core/init.c"

[cpp]
struct snd_card *snd_cards[SNDRV_CARDS]; 

struct snd_card *snd_cards[SNDRV_CARDS];SNDRV_CARDS為8也就是聲卡最多8個

3.聲卡設備結構體

[cpp]
struct snd_device { 
    struct list_head list;      //鏈表  
    struct snd_card *card;      //所屬的聲卡  
    snd_device_state_t state;   //設備狀態  
    snd_device_type_t type;     //設備類型  
    void *device_data;      /* device structure */ 
    struct snd_device_ops *ops; //聲卡設備操作函數集  
}; 

struct snd_device {
 struct list_head list;  //鏈表
 struct snd_card *card;  //所屬的聲卡
 snd_device_state_t state; //設備狀態
 snd_device_type_t type;  //設備類型
 void *device_data;  /* device structure */
 struct snd_device_ops *ops; //聲卡設備操作函數集
};3.1 設備狀態的值

[cpp]
#define SNDRV_DEV_BUILD     ((__force snd_device_state_t) 0)//創建  
#define SNDRV_DEV_REGISTERED    ((__force snd_device_state_t) 1)//注冊  
#define SNDRV_DEV_DISCONNECTED  ((__force snd_device_state_t) 2)//斷開連接 

#define SNDRV_DEV_BUILD  ((__force snd_device_state_t) 0)//創建
#define SNDRV_DEV_REGISTERED ((__force snd_device_state_t) 1)//注冊
#define SNDRV_DEV_DISCONNECTED ((__force snd_device_state_t) 2)//斷開連接3.2 設備類型
也就是上面 1.聲卡設備類型定義 所指定的類型

4.聲卡操作函數集

[cpp] view plaincopyprint?struct snd_device_ops { 
    int (*dev_free)(struct snd_device *dev);        //釋放  
    int (*dev_register)(struct snd_device *dev);    //注冊  
    int (*dev_disconnect)(struct snd_device *dev);  //斷開連接  
}; 

struct snd_device_ops {
 int (*dev_free)(struct snd_device *dev);  //釋放
 int (*dev_register)(struct snd_device *dev); //注冊
 int (*dev_disconnect)(struct snd_device *dev); //斷開連接
};
第二部分 聲卡

1.聲卡創建

傳遞進來的idx為負值,則系統會分配一個idx作為全局snd_cards數組的索引項值,xid字串用來區分描述聲卡id,module一般為THIS_MODULE...

[cpp]
int snd_card_create(int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret) 

    struct snd_card *card; 
    int err, idx2; 
    if (snd_BUG_ON(!card_ret)) 
        return -EINVAL; 
    *card_ret = NULL; 
    if (extra_size < 0) 
        extra_size = 0; 
    card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); //分配聲卡對象和額外空間的內存  
    if (!card) 
        return -ENOMEM; 
    if (xid)    //若需要填充聲卡id識別字串  
        strlcpy(card->id, xid, sizeof(card->id)); //card->id=xid 聲卡id識別字串  
    err = 0; 
     
    mutex_lock(&snd_card_mutex);    //idx為負值則交由系統選擇一個值______________________<  
    if (idx < 0) {    
        for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) 
            /* idx == -1 == 0xffff means: take any free slot */ 
            if (~snd_cards_lock & idx & 1<<idx2) { 
                if (module_slot_match(module, idx2)) { 
                    idx = idx2; 
                    break; 
                } 
            } 
    } 
    if (idx < 0) { 
        for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) 
            /* idx == -1 == 0xffff means: take any free slot */ 
            if (~snd_cards_lock & idx & 1<<idx2) { 
                if (!slots[idx2] || !*slots[idx2]) { 
                    idx = idx2; 
                    break; 
                } 
            } 
    } 
    if (idx < 0) 
        err = -ENODEV; 
    else if (idx < snd_ecards_limit) { 
        if (snd_cards_lock & (1 << idx)) 
            err = -EBUSY;   /* invalid */ 
    } else if (idx >= SNDRV_CARDS) 
        err = -ENODEV; 
    if (err < 0) { 
        mutex_unlock(&snd_card_mutex); 
        snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",idx, snd_ecards_limit - 1, err); 
        goto __error; 
    } 
    snd_cards_lock |= 1 << idx;       /* lock it */ 
    if (idx >= snd_ecards_limit) 
        snd_ecards_limit = idx + 1; /* increase the limit */ 
    mutex_unlock(&snd_card_mutex);      //______________________>  
     
    card->number = idx;      //聲卡對象索引號 全局數組snd_cards的數組下標  
    card->module = module;   //聲卡對象模塊所有者THIS_MODULE  
    INIT_LIST_HEAD(&card->devices);  //初始化聲卡設備鏈表  
    init_rwsem(&card->controls_rwsem);   //初始化讀寫信號量  
    rwlock_init(&card->ctl_files_rwlock);    //初始化讀寫鎖  
    INIT_LIST_HEAD(&card->controls); //初始化聲卡控制鏈表  
    INIT_LIST_HEAD(&card->ctl_files);    //初始化聲卡控制文件鏈表  
    spin_lock_init(&card->files_lock);   //初始化自旋鎖  
    INIT_LIST_HEAD(&card->files_list);   //初始化聲卡文件鏈表  
    init_waitqueue_head(&card->shutdown_sleep);  //初始化關機隊列頭  
#ifdef CONFIG_PM  
    mutex_init(&card->power_lock);   //初始化電源互斥鎖  
    init_waitqueue_head(&card->power_sleep); //初始化睡眠等待隊列頭  
#endif  
    err = snd_ctl_create(card); //創建用於控制的聲卡設備對象  
    if (err < 0) { 
        snd_printk(KERN_ERR "unable to register control minors\n"); 
        goto __error; 
    } 
    err = snd_info_card_create(card);   //proc下面的接口  
    if (err < 0) { 
        snd_printk(KERN_ERR "unable to create card info\n"); 
        goto __error_ctl; 
    } 
    if (extra_size > 0)  //有額外數據  
        card->private_data = (char *)card + sizeof(struct snd_card); 
    *card_ret = card; 
    return 0; 
 
      __error_ctl: 
    snd_device_free_all(card, SNDRV_DEV_CMD_PRE); 
      __error: 
    kfree(card); 
    return err; 

EXPORT_SYMBOL(snd_card_create); 

int snd_card_create(int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret)
{
 struct snd_card *card;
 int err, idx2;
 if (snd_BUG_ON(!card_ret))
  return -EINVAL;
 *card_ret = NULL;
 if (extra_size < 0)
  extra_size = 0;
 card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); //分配聲卡對象和額外空間的內存
 if (!card)
  return -ENOMEM;
 if (xid) //若需要填充聲卡id識別字串
  strlcpy(card->id, xid, sizeof(card->id)); //card->id=xid 聲卡id識別字串
 err = 0;
 
 mutex_lock(&snd_card_mutex); //idx為負值則交由系統選擇一個值______________________<
 if (idx < 0) { 
  for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
   /* idx == -1 == 0xffff means: take any free slot */
   if (~snd_cards_lock & idx & 1<<idx2) {
    if (module_slot_match(module, idx2)) {
     idx = idx2;
     break;
    }
   }
 }
 if (idx < 0) {
  for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
   /* idx == -1 == 0xffff means: take any free slot */
   if (~snd_cards_lock & idx & 1<<idx2) {
    if (!slots[idx2] || !*slots[idx2]) {
     idx = idx2;
     break;
    }
   }
 }
 if (idx < 0)
  err = -ENODEV;
 else if (idx < snd_ecards_limit) {
  if (snd_cards_lock & (1 << idx))
   err = -EBUSY; /* invalid */
 } else if (idx >= SNDRV_CARDS)
  err = -ENODEV;
 if (err < 0) {
  mutex_unlock(&snd_card_mutex);
  snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",idx, snd_ecards_limit - 1, err);
  goto __error;
 }
 snd_cards_lock |= 1 << idx;  /* lock it */
 if (idx >= snd_ecards_limit)
  snd_ecards_limit = idx + 1; /* increase the limit */
 mutex_unlock(&snd_card_mutex);  //______________________>
 
 card->number = idx;  //聲卡對象索引號 全局數組snd_cards的數組下標
 card->module = module; //聲卡對象模塊所有者THIS_MODULE
 INIT_LIST_HEAD(&card->devices); //初始化聲卡設備鏈表
 init_rwsem(&card->controls_rwsem); //初始化讀寫信號量
 rwlock_init(&card->ctl_files_rwlock); //初始化讀寫鎖
 INIT_LIST_HEAD(&card->controls); //初始化聲卡控制鏈表
 INIT_LIST_HEAD(&card->ctl_files); //初始化聲卡控制文件鏈表
 spin_lock_init(&card->files_lock); //初始化自旋鎖
 INIT_LIST_HEAD(&card->files_list); //初始化聲卡文件鏈表
 init_waitqueue_head(&card->shutdown_sleep); //初始化關機隊列頭
#ifdef CONFIG_PM
 mutex_init(&card->power_lock); //初始化電源互斥鎖
 init_waitqueue_head(&card->power_sleep); //初始化睡眠等待隊列頭
#endif
 err = snd_ctl_create(card); //創建用於控制的聲卡設備對象
 if (err < 0) {
  snd_printk(KERN_ERR "unable to register control minors\n");
  goto __error;
 }
 err = snd_info_card_create(card); //proc下面的接口
 if (err < 0) {
  snd_printk(KERN_ERR "unable to create card info\n");
  goto __error_ctl;
 }
 if (extra_size > 0) //有額外數據
  card->private_data = (char *)card + sizeof(struct snd_card);
 *card_ret = card;
 return 0;

      __error_ctl:
 snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
      __error:
 kfree(card);
   return err;
}
EXPORT_SYMBOL(snd_card_create);這裡主要是初始化了聲卡的devices聲卡設備鏈表,以後創建的聲卡設備將掛在該鏈表上

並調用了snd_ctl_create創建了用於控制的聲卡設備對象,這我們可以得出一個結論每個聲卡都有一個聲卡控制設備對象

\


第三部分 聲卡設備

1.創建聲卡設備

在這裡 聲卡設備與聲卡捆綁,指定聲卡設備類型,設置聲卡設備狀態,捆綁對應的snd_device_ops方法,添加聲卡設備到聲卡的devices鏈表

[cpp]
int snd_device_new(struct snd_card *card, snd_device_type_t type,void *device_data, struct snd_device_ops *ops) 

    struct snd_device *dev; 
 
    if (snd_BUG_ON(!card || !device_data || !ops)) 
        return -ENXIO; 
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);    //分配聲卡設備內存  
    if (dev == NULL) { 
        snd_printk(KERN_ERR "Cannot allocate device\n"); 
        return -ENOMEM; 
    } 
    dev->card = card;    //設備捆綁對象  
    dev->type = type;    //指定設備類型  
    dev->state = SNDRV_DEV_BUILD;    //設備狀態 已創建  
    dev->device_data = device_data;  //設備數據  
    dev->ops = ops;  //設備操作函數集  
    list_add(&dev->list, &card->devices); //添加到聲卡對象的設備devices鏈表中  
    return 0; 

EXPORT_SYMBOL(snd_device_new); 

int snd_device_new(struct snd_card *card, snd_device_type_t type,void *device_data, struct snd_device_ops *ops)
{
 struct snd_device *dev;

 if (snd_BUG_ON(!card || !device_data || !ops))
  return -ENXIO;
 dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配聲卡設備內存
 if (dev == NULL) {
  snd_printk(KERN_ERR "Cannot allocate device\n");
  return -ENOMEM;
 }
 dev->card = card; //設備捆綁對象
 dev->type = type; //指定設備類型
 dev->state = SNDRV_DEV_BUILD; //設備狀態 已創建
 dev->device_data = device_data; //設備數據
 dev->ops = ops; //設備操作函數集
 list_add(&dev->list, &card->devices); //添加到聲卡對象的設備devices鏈表中
 return 0;
}
EXPORT_SYMBOL(snd_device_new);這裡聲卡設備的snd_device_ops是傳遞進來的結構體指針,實際操作中一般調用以下API來創建不同類型的聲卡

1.1 創建聲卡設備常見API

[cpp]
snd_ctl_create      -->snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); 
snd_card_proc_new   -->snd_device_new(card, SNDRV_DEV_INFO, entry, &ops) 
snd_timer_new       -->snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops) 
snd_jack_new        -->snd_device_new(card, SNDRV_DEV_JACK, jack, &ops) 
snd_pcm_new         -->snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops) 
snd_rawmidi_new     -->snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops) 
snd_seq_device_new  -->snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops) 
snd_hwdep_new       -->snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops) 
snd_i2c_bus_create  -->snd_device_new(card, SNDRV_DEV_BUS, bus, &ops) 
snd_hda_bus_new     -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops) 
snd_ac97_bus        -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops) 
...//不完整 

snd_ctl_create  -->snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
snd_card_proc_new -->snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)
snd_timer_new  -->snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)
snd_jack_new  -->snd_device_new(card, SNDRV_DEV_JACK, jack, &ops)
snd_pcm_new   -->snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
snd_rawmidi_new  -->snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)
snd_seq_device_new -->snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)
snd_hwdep_new  -->snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)
snd_i2c_bus_create -->snd_device_new(card, SNDRV_DEV_BUS, bus, &ops)
snd_hda_bus_new  -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)
snd_ac97_bus  -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)
...//不完整這些API都靜態設置了對應類型的snd_device_ops結構體ops,然後調用snd_device_new並將ops傳遞進來
後面我們再針對不同的聲卡設備類型展開分析

創建聲卡設備後系統大致入下圖描述

 

創建後的聲卡設備的state值為SNDRV_DEV_BUILD

 

第四部分 注冊聲卡

1.注冊聲卡

[cpp]
int snd_card_register(struct snd_card *card) 

    int err; 
    if (snd_BUG_ON(!card)) 
        return -EINVAL; 
    if (!card->card_dev) { 
        //創建設備文件"/sys/class/sound/cardX"  
        card->card_dev = device_create(sound_class, card->dev,MKDEV(0, 0), card,"card%i", card->number); 
        if (IS_ERR(card->card_dev)) 
            card->card_dev = NULL; 
    } 
 
    if ((err = snd_device_register_all(card)) < 0)   //-->1.1.注冊所有的聲卡設備  
        return err; 
    mutex_lock(&snd_card_mutex); 
    if (snd_cards[card->number]) {   //判斷對應數組項是否已給占用"/sound/core/init.c" struct snd_card *snd_cards[SNDRV_CARDS];  
        mutex_unlock(&snd_card_mutex); 
        return 0; 
    } 
    snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); 
    snd_cards[card->number] = card;  //填充全局snd_cards數組  
    mutex_unlock(&snd_card_mutex); 
    init_info_for_card(card);   //proc接口  
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)  
    if (snd_mixer_oss_notify_callback) 
        snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); 
#endif  
    if (card->card_dev) {    //創建屬性文件  
        err = device_create_file(card->card_dev, &card_id_attrs); 
        if (err < 0) 
            return err; 
        err = device_create_file(card->card_dev, &card_number_attrs); 
        if (err < 0) 
            return err; 
    } 
 
    return 0; 

EXPORT_SYMBOL(snd_card_register); 

int snd_card_register(struct snd_card *card)
{
 int err;
 if (snd_BUG_ON(!card))
  return -EINVAL;
 if (!card->card_dev) {
  //創建設備文件"/sys/class/sound/cardX"
  card->card_dev = device_create(sound_class, card->dev,MKDEV(0, 0), card,"card%i", card->number);
  if (IS_ERR(card->card_dev))
   card->card_dev = NULL;
 }

 if ((err = snd_device_register_all(card)) < 0) //-->1.1.注冊所有的聲卡設備
  return err;
 mutex_lock(&snd_card_mutex);
 if (snd_cards[card->number]) { //判斷對應數組項是否已給占用"/sound/core/init.c" struct snd_card *snd_cards[SNDRV_CARDS];
  mutex_unlock(&snd_card_mutex);
  return 0;
 }
 snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
 snd_cards[card->number] = card; //填充全局snd_cards數組
 mutex_unlock(&snd_card_mutex);
 init_info_for_card(card); //proc接口
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
 if (snd_mixer_oss_notify_callback)
  snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
 if (card->card_dev) { //創建屬性文件
  err = device_create_file(card->card_dev, &card_id_attrs);
  if (err < 0)
   return err;
  err = device_create_file(card->card_dev, &card_number_attrs);
  if (err < 0)
   return err;
 }

 return 0;
}
EXPORT_SYMBOL(snd_card_register);主要是調用snd_device_register_all函數注冊所有聲卡設備,其次是填充了全局snd_cards數組對應的數組項
1.1注冊掛在該聲卡下面的所有聲卡設備

[cpp]
int snd_device_register_all(struct snd_card *card) 

    struct snd_device *dev; 
    int err; 
     
    if (snd_BUG_ON(!card)) 
        return -ENXIO; 
    list_for_each_entry(dev, &card->devices, list) { //遍歷聲卡的設備devices鏈表  
        if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {//狀態為已建立且存在dev_register方法  
            if ((err = dev->ops->dev_register(dev)) < 0)   //調用聲卡設備的dev_register方法  
                return err; 
            dev->state = SNDRV_DEV_REGISTERED;   //修改狀態為已注冊  
        } 
    } 
    return 0; 

int snd_device_register_all(struct snd_card *card)
{
 struct snd_device *dev;
 int err;
 
 if (snd_BUG_ON(!card))
  return -ENXIO;
 list_for_each_entry(dev, &card->devices, list) { //遍歷聲卡的設備devices鏈表
  if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {//狀態為已建立且存在dev_register方法
   if ((err = dev->ops->dev_register(dev)) < 0) //調用聲卡設備的dev_register方法
    return err;
   dev->state = SNDRV_DEV_REGISTERED; //修改狀態為已注冊
  }
 }
 return 0;
}這裡會遍歷聲卡對象的devices設備鏈表,然後調用聲卡設備所捆綁的聲卡設備操作函數集合的dev_register方法,注冊初始化對應的聲卡設備

注冊完聲卡後,聲卡設備的state值修改為SNDRV_DEV_REGISTERED


 

第五部分 注冊聲卡設備

不同類型的聲卡設備的操作函數集的dev_register不同,但是也有其共性,下面主要是針對共性來分析

1.snd_minor聲卡字符設備結構體


[cpp]
struct snd_minor { 
    int type;           //聲卡設備類型 SNDRV_DEVICE_TYPE_XXX  
    int card;           //聲卡索引號  
    int device;         /* device number */ 
    const struct file_operations *f_ops;    //文件操作函數集  
    void *private_data;     //私有數據  
    struct device *dev;     //設備文件  
}; 

struct snd_minor {
 int type;   //聲卡設備類型 SNDRV_DEVICE_TYPE_XXX
 int card;   //聲卡索引號
 int device;   /* device number */
 const struct file_operations *f_ops; //文件操作函數集
 void *private_data;  //私有數據
 struct device *dev;  //設備文件
};1.1 snd_minor的type類型[cpp] view plaincopyprint?enum { 
    SNDRV_DEVICE_TYPE_CONTROL,          //控制  
    SNDRV_DEVICE_TYPE_SEQUENCER,        //音序器  
    SNDRV_DEVICE_TYPE_TIMER,            //定時器  
    SNDRV_DEVICE_TYPE_HWDEP,            //硬件依賴層  
    SNDRV_DEVICE_TYPE_RAWMIDI,          //raw midi  
    SNDRV_DEVICE_TYPE_PCM_PLAYBACK,     //PCM回放  
    SNDRV_DEVICE_TYPE_PCM_CAPTURE,      //PCM捕捉  
}; 

enum {
 SNDRV_DEVICE_TYPE_CONTROL,   //控制
 SNDRV_DEVICE_TYPE_SEQUENCER,  //音序器
 SNDRV_DEVICE_TYPE_TIMER,   //定時器
 SNDRV_DEVICE_TYPE_HWDEP,   //硬件依賴層
 SNDRV_DEVICE_TYPE_RAWMIDI,   //raw midi
 SNDRV_DEVICE_TYPE_PCM_PLAYBACK,  //PCM回放
 SNDRV_DEVICE_TYPE_PCM_CAPTURE,  //PCM捕捉
};1.2 全局snd_minors全局數組,數組項最大值SNDRV_OS_MINORS為256
[cpp]
static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; 

static struct snd_minor *snd_minors[SNDRV_OS_MINORS];

 

 

2.注冊設備文件snd_register_device
封裝了(2.1)snd_register_device_for_dev函數
[cpp]
static inline int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data,const char *name) 

    return snd_register_device_for_dev(type, card, dev, f_ops,private_data, name,snd_card_get_device_link(card)); 

static inline int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data,const char *name)
{
 return snd_register_device_for_dev(type, card, dev, f_ops,private_data, name,snd_card_get_device_link(card));
}2.1注冊設備文件

[cpp]
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,const struct file_operations *f_ops, 
                void *private_data,const char *name, struct device *device) 

    int minor;  //次設備號  
    struct snd_minor *preg; //聲明一個snd_minor結構體  
 
    if (snd_BUG_ON(!name)) 
        return -EINVAL; 
    preg = kmalloc(sizeof *preg, GFP_KERNEL);   //分配snd_minor結構體內存  
    if (preg == NULL) 
        return -ENOMEM; 
    preg->type = type;   //設置snd_minor類型  
    preg->card = card ? card->number : -1;    //聲卡索引號  
    preg->device = dev;  //設備文件  
    preg->f_ops = f_ops; //文件操作函數集合  
    preg->private_data = private_data;   //私有數據  
    mutex_lock(&sound_mutex); 
#ifdef CONFIG_SND_DYNAMIC_MINORS  
    minor = snd_find_free_minor(); 
#else  
    minor = snd_kernel_minor(type, card, dev);  //獲取次設備號  
    if (minor >= 0 && snd_minors[minor]) 
        minor = -EBUSY; 
#endif  
    if (minor < 0) { 
        mutex_unlock(&sound_mutex); 
        kfree(preg); 
        return minor; 
    } 
    snd_minors[minor] = preg;   //填充全局snd_minors數組項  
    preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name);    //創建"/dev/snd/XXX"  
    if (IS_ERR(preg->dev)) { 
        snd_minors[minor] = NULL; 
        mutex_unlock(&sound_mutex); 
        minor = PTR_ERR(preg->dev); 
        kfree(preg); 
        return minor; 
    } 
    mutex_unlock(&sound_mutex); 
    return 0; 

EXPORT_SYMBOL(snd_register_device_for_dev); 

int snd_register_device_for_dev(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,
    void *private_data,const char *name, struct device *device)
{
 int minor; //次設備號
 struct snd_minor *preg; //聲明一個snd_minor結構體

 if (snd_BUG_ON(!name))
  return -EINVAL;
 preg = kmalloc(sizeof *preg, GFP_KERNEL); //分配snd_minor結構體內存
 if (preg == NULL)
  return -ENOMEM;
 preg->type = type; //設置snd_minor類型
 preg->card = card ? card->number : -1; //聲卡索引號
 preg->device = dev; //設備文件
 preg->f_ops = f_ops; //文件操作函數集合
 preg->private_data = private_data; //私有數據
 mutex_lock(&sound_mutex);
#ifdef CONFIG_SND_DYNAMIC_MINORS
 minor = snd_find_free_minor();
#else
 minor = snd_kernel_minor(type, card, dev); //獲取次設備號
 if (minor >= 0 && snd_minors[minor])
  minor = -EBUSY;
#endif
 if (minor < 0) {
  mutex_unlock(&sound_mutex);
  kfree(preg);
  return minor;
 }
 snd_minors[minor] = preg; //填充全局snd_minors數組項
 preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name); //創建"/dev/snd/XXX"
 if (IS_ERR(preg->dev)) {
  snd_minors[minor] = NULL;
  mutex_unlock(&sound_mutex);
  minor = PTR_ERR(preg->dev);
  kfree(preg);
  return minor;
 }
 mutex_unlock(&sound_mutex);
 return 0;
}
EXPORT_SYMBOL(snd_register_device_for_dev);主要是獲取次設備號,並創建設備文件,捆綁文件操作函數集合

第六部分 聲卡驅動的編寫框架

 

綜合上面五個部分得出下圖

 

\


編寫過程為先調用snd_card_create創建聲卡,接著調用創建聲卡設備的API創建不同類型的聲卡設備組件,接著調用snd_card_register注冊聲卡就行.

 大致走完上面的流程後系統的框圖

 

\
 

第七部分 聲卡核心子系統的初始化工作

1.聲明子系統

[cpp]
subsys_initcall(init_soundcore); 

subsys_initcall(init_soundcore);2.子系統初始化

[cpp]
static int __init init_soundcore(void) 

    int rc; 
    rc = init_oss_soundcore();//初始化oss子系統部分  
    if (rc) 
        return rc; 
    sound_class = class_create(THIS_MODULE, "sound");   //創建設備類"/sys/class/sound/"  
    if (IS_ERR(sound_class)) { 
        cleanup_oss_soundcore(); 
        return PTR_ERR(sound_class); 
    } 
    sound_class->devnode = sound_devnode;    //創建設備節點的方法  
    return 0; 

static int __init init_soundcore(void)
{
 int rc;
 rc = init_oss_soundcore();//初始化oss子系統部分
 if (rc)
  return rc;
 sound_class = class_create(THIS_MODULE, "sound"); //創建設備類"/sys/class/sound/"
 if (IS_ERR(sound_class)) {
  cleanup_oss_soundcore();
  return PTR_ERR(sound_class);
 }
 sound_class->devnode = sound_devnode; //創建設備節點的方法
 return 0;
}主要初始化oss子系統部分
2.1 oss子系統初始化

[cpp]
static int __init init_oss_soundcore(void) 

    if (preclaim_oss && register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {//創建字符設備  
        printk(KERN_ERR "soundcore: sound device already in use.\n"); 
        return -EBUSY; 
    } 
    return 0; 

static int __init init_oss_soundcore(void)
{
 if (preclaim_oss && register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {//創建字符設備
  printk(KERN_ERR "soundcore: sound device already in use.\n");
  return -EBUSY;
 }
 return 0;
}2.2 指定sound_class類的創建設備節點方法sound_devnode

[cpp]
static char *sound_devnode(struct device *dev, mode_t *mode) 

    if (MAJOR(dev->devt) == SOUND_MAJOR) //主設備號14 oss子系統  
        return NULL; 
    return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));  //alsa子系統 "/dev/sndX/"  

static char *sound_devnode(struct device *dev, mode_t *mode)
{
 if (MAJOR(dev->devt) == SOUND_MAJOR) //主設備號14 oss子系統
  return NULL;
 return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev)); //alsa子系統 "/dev/sndX/"
}所以alsa子系統的音頻設備會出現在/dev/snd/目錄下
這裡我們可以得知alsa架構的設備節點在/dev/snd/目錄下,oss架構的設備節點在/dev下

alsa的主設備號為116,oss架構的主設備號為14

alsa的主設備號在/sound/core/sound.c中定義

[cpp]
static int major = CONFIG_SND_MAJOR; 

static int major = CONFIG_SND_MAJOR;[cpp] view plaincopyprint?#define CONFIG_SND_MAJOR    116 

#define CONFIG_SND_MAJOR 116
第八部分 聲卡控制設備淺析

前面講到每個聲卡都有一個聲卡控制設備對象,所以研究下聲卡控制設備

在聲卡創建函數snd_card_create中調用了snd_ctl_create函數創建聲卡控制設備,並將聲卡對象作為參數傳遞進來

1.創建聲卡控制設備

[cpp]
int snd_ctl_create(struct snd_card *card) 

    static struct snd_device_ops ops = {//靜態初始化snd_device_ops聲卡設備操作函數集結構體  
        .dev_free = snd_ctl_dev_free,//釋放方法  
        .dev_register = snd_ctl_dev_register,//注冊方法  
        .dev_disconnect = snd_ctl_dev_disconnect,//斷開連接方法  
    }; 
 
    if (snd_BUG_ON(!card)) 
        return -ENXIO; 
    return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//創建聲卡控制設備  

int snd_ctl_create(struct snd_card *card)
{
 static struct snd_device_ops ops = {//靜態初始化snd_device_ops聲卡設備操作函數集結構體
  .dev_free = snd_ctl_dev_free,//釋放方法
  .dev_register = snd_ctl_dev_register,//注冊方法
  .dev_disconnect = snd_ctl_dev_disconnect,//斷開連接方法
 };

 if (snd_BUG_ON(!card))
  return -ENXIO;
 return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//創建聲卡控制設備
}這裡還需注意一下snd_device_new函數的參數,發現聲卡控制設備的device_data是指向聲卡對象的

在注冊聲卡過程中會調用snd_device_register_all函數,該函數則調用聲卡控制設備dev_register方法,既snd_ctl_dev_register函數

2.注冊聲卡控制設備

[cpp]
static int snd_ctl_dev_register(struct snd_device *device) 

    struct snd_card *card = device->device_data; //獲取聲卡對象  
    int err, cardnum; 
    char name[16]; 
 
    if (snd_BUG_ON(!card)) 
        return -ENXIO; 
    cardnum = card->number;  //獲取聲卡索引號  
    if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) 
        return -ENXIO; 
    sprintf(name, "controlC%i", cardnum);   //設置名字-->"/dev/snd/controlCx"  
    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name)) < 0) //注冊聲卡控制設備  
        return err; 
    return 0; 

static int snd_ctl_dev_register(struct snd_device *device)
{
 struct snd_card *card = device->device_data; //獲取聲卡對象
 int err, cardnum;
 char name[16];

 if (snd_BUG_ON(!card))
  return -ENXIO;
 cardnum = card->number; //獲取聲卡索引號
 if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  return -ENXIO;
 sprintf(name, "controlC%i", cardnum); //設置名字-->"/dev/snd/controlCx"
 if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name)) < 0) //注冊聲卡控制設備
  return err;
 return 0;
}通過snd_register_device創建了/dev/snd/controlC0設備文件,假設是0號聲卡吧!並捆綁了snd_ctl_f_ops設備文件操作函數集

3.聲卡控制設備對應的設備文件操作函數集

[cpp]
static const struct file_operations snd_ctl_f_ops = 

    .owner =    THIS_MODULE, 
    .read =     snd_ctl_read,   //讀方法  
    .open =     snd_ctl_open,   //打開方法  
    .release =  snd_ctl_release,    //釋放方法  
    .llseek =   no_llseek, 
    .poll =     snd_ctl_poll,   //輪詢方法  
    .unlocked_ioctl =   snd_ctl_ioctl,  //命令控制  
    .compat_ioctl = snd_ctl_ioctl_compat,   //32位兼容的命令控制  
    .fasync =   snd_ctl_fasync, //同步方法  
}; 

static const struct file_operations snd_ctl_f_ops =
{
 .owner = THIS_MODULE,
 .read =  snd_ctl_read, //讀方法
 .open =  snd_ctl_open, //打開方法
 .release = snd_ctl_release, //釋放方法
 .llseek = no_llseek,
 .poll =  snd_ctl_poll, //輪詢方法
 .unlocked_ioctl = snd_ctl_ioctl, //命令控制
 .compat_ioctl = snd_ctl_ioctl_compat, //32位兼容的命令控制
 .fasync = snd_ctl_fasync, //同步方法
};這樣就提供了應用層的接口方法了,比較重要的是命令控制方法,主要有以下控制命令

[cpp]

#define SNDRV_CTL_IOCTL_PVERSION    _IOR('U', 0x00, int)//打印alsa版本  
#define SNDRV_CTL_IOCTL_CARD_INFO   _IOR('U', 0x01, struct snd_ctl_card_info)//獲取聲卡信息  
#define SNDRV_CTL_IOCTL_ELEM_LIST   _IOWR('U', 0x10, struct snd_ctl_elem_list)  
#define SNDRV_CTL_IOCTL_ELEM_INFO   _IOWR('U', 0x11, struct snd_ctl_elem_info)  
#define SNDRV_CTL_IOCTL_ELEM_READ   _IOWR('U', 0x12, struct snd_ctl_elem_value)  
#define SNDRV_CTL_IOCTL_ELEM_WRITE  _IOWR('U', 0x13, struct snd_ctl_elem_value)  
#define SNDRV_CTL_IOCTL_ELEM_LOCK   _IOW('U', 0x14, struct snd_ctl_elem_id)  
#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id)  
#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int)  
#define SNDRV_CTL_IOCTL_ELEM_ADD    _IOWR('U', 0x17, struct snd_ctl_elem_info)  
#define SNDRV_CTL_IOCTL_ELEM_REPLACE    _IOWR('U', 0x18, struct snd_ctl_elem_info)  
#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id)  
#define SNDRV_CTL_IOCTL_TLV_READ    _IOWR('U', 0x1a, struct snd_ctl_tlv)  
#define SNDRV_CTL_IOCTL_TLV_WRITE   _IOWR('U', 0x1b, struct snd_ctl_tlv)  
#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv)  
#define SNDRV_CTL_IOCTL_POWER   _IOWR('U', 0xd0, int)//還沒支持  
#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)//電源狀態 

#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)//打印alsa版本
#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)//獲取聲卡信息
#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list)
#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info)
#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value)
#define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value)
#define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id)
#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id)
#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int)
#define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info)
#define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info)
#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id)
#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv)
#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv)
#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv)
#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int)//還沒支持
#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)//電源狀態如何與應用層交互參考alsa-lib的說明 alsa提供了很多工具可以使用
alsa-utils工具集

[cpp]
aconnect        is a utility for connecting and disconnecting two existing ports in the ALSA sequencer system.   
alsaconf        is a configuration tool which tries to detect the sound cards on your system and write a suitable configuration file for ALSA. This program is incompatible with Udev.  
alsactl         is used to control advanced settings for the ALSA sound card drivers.   
alsaloop        allows creation of a PCM loopback between a PCM capture device and a PCM playback device.  
alsamixer       is an Ncurses based mixer program for use with the ALSA sound card drivers.  
amidi           is used to read from and write to ALSA RawMIDI ports.  
amixer          allows command-line control of the mixers for the ALSA sound card drivers.  
aplay           is a command-line soundfile player for the ALSA sound card drivers.  
aplaymidi       is a command-line utility that plays the specified MIDI file(s) to one or more ALSA sequencer ports.  
arecord         is a command-line soundfile recorder for the ALSA sound card drivers.  
arecordmidi     is a command-line utility that records a standard MIDI file from one or more ALSA sequencer ports.  
aseqdump        is a command-line utility that prints the sequencer events it receives as text.   
aseqnet         is an ALSA sequencer client which sends and receives event packets over a network.  
iecset          is a small utility to set or dump the IEC958 (or so-called “S/PDIF”) status bits of the specified sound card via the ALSA control API.  
speaker-test    is a command-line speaker test tone generator for ALSA. 

aconnect    is a utility for connecting and disconnecting two existing ports in the ALSA sequencer system. 
alsaconf    is a configuration tool which tries to detect the sound cards on your system and write a suitable configuration file for ALSA. This program is incompatible with Udev.
alsactl    is used to control advanced settings for the ALSA sound card drivers. 
alsaloop    allows creation of a PCM loopback between a PCM capture device and a PCM playback device.
alsamixer    is an Ncurses based mixer program for use with the ALSA sound card drivers.
amidi     is used to read from and write to ALSA RawMIDI ports.
amixer    allows command-line control of the mixers for the ALSA sound card drivers.
aplay     is a command-line soundfile player for the ALSA sound card drivers.
aplaymidi   is a command-line utility that plays the specified MIDI file(s) to one or more ALSA sequencer ports.
arecord    is a command-line soundfile recorder for the ALSA sound card drivers.
arecordmidi  is a command-line utility that records a standard MIDI file from one or more ALSA sequencer ports.
aseqdump    is a command-line utility that prints the sequencer events it receives as text. 
aseqnet   is an ALSA sequencer client which sends and receives event packets over a network.
iecset    is a small utility to set or dump the IEC958 (or so-called “S/PDIF”) status bits of the specified sound card via the ALSA control API.
speaker-test  is a command-line speaker test tone generator for ALSA.還有一個帶圖形界面的alsamixer工具,相當強大

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved