Linux下ALSA聲卡編程

. 介紹

      ALSA 標準是一個先進的linux聲音體系。它包含內核驅動集合,API庫和工具對Linux聲音進行支持。ALSA 包含一系列內核驅動對不同的聲卡進行支持,還提供了libasoundAPI庫。用這些進行寫程序不需要打開設備等操作,所以編程人員在寫程序的時候不會被底層的東西困擾。與此相反OSS/Free 驅動在內核層次調用,需要指定設備名和調用ioctl。爲提供向後兼容, ALSA 提供內核模塊模仿 OSS/Free 驅動,所以大多數的程序不需要改動。 ALSA 擁有調用插件的能力對新設備提供擴展,包括那些用軟件模擬出來的虛擬設備。 ALSA 還提供一組命令行工具包括  mixer, sound file player 和工具控制一些特別的聲卡的特別的作用。

 

.ALSA 體系:

ALSA API 被主要分爲以下幾種接口:

l         控制接口:提供靈活的方式管理註冊的聲卡和對存在的聲卡進行查詢。

l         PCM接口:提供管理數字音頻的捕捉和回放。

l         原始 MIDI 接口: 支持 MIDI (Musical Instrument Digital Interface), 一種標準電子音樂指令集。 這些 API 提供訪問聲卡上的 MIDI 總線。這些原始藉口直接工作在 The  MIDI 事件上,程序員只需要管理協議和時間。

l         記時接口: 爲支持聲音的同步事件提供訪問聲卡上的定時器。

l         音序器接口:一個比原始MIDI接口高級的MIDI編程和聲音同步高層接口。它可以處理很多的MIDI協議和定時器。

l         混音器接口:控制發送信號和控制聲音大小的聲卡上的設備。

 

.聲卡的緩存和數據的傳輸:

      一塊聲卡有一個聲卡內存用來存儲記錄的樣本。當它被寫滿時就產生中斷。內核驅動就使用DMA將數據傳輸到內存中。同樣地,當在播放時就將內存中的聲音樣本使用DMA傳到聲卡的內存中!

      聲卡的緩存是環狀的,這裏只討論應用程序中的內存結構:ALSA將數據分成連續的片段然後傳到按單元片段傳輸。

 

四:典型的聲音程序結構:

  1.         open interface for capture or playback
  2.         set hardware parameters
  3.         (access mode, data format, channels, rate, etc.)
  4.         while there is data to be processed:
  5.         read PCM data (capture)
  6.         or write PCM data (playback)
  7.         close interface

 

.一些例子:

1.顯示一些PCM的類型和格式:

 

  1. #include <iostream>
  2. #include <alsa/asoundlib.h> 
  3. int main()
  4. {
  5.        std::cout << "ALSA library version: " << SND_LIB_VERSION_STR << std::endl;
  6.  
  7.        std::cout << "PCM stream types: " << std::endl;
  8.  
  9.        for (int val=0; val <= SND_PCM_STREAM_LAST; ++val)
  10.               std::cout << snd_pcm_stream_name((snd_pcm_stream_t)val) << std::endl;
  11.        std::cout << std::endl; 
  12.        std::cout << "PCM access types: " << std::endl;
  13.        for (int val=0; val <= SND_PCM_ACCESS_LAST; ++val)
  14.               std::cout << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;
  15.        std::cout << std::endl; 
  16.        std::cout << "PCM subformats: " << std::endl;
  17.        for (int val=0; val <= SND_PCM_SUBFORMAT_LAST; ++val)
  18.               std::cout << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << " (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;
  19.        std::cout << std::endl; 
  20.        std::cout << "PCM states: " << std::endl;
  21.        for (int val=0; val <= SND_PCM_STATE_LAST; ++val)
  22.               std::cout << snd_pcm_state_name((snd_pcm_state_t)val) << std::endl;
  23.        std::cout << std::endl; 
  24.        std::cout << "PCM formats: " << std::endl;
  25.        for (int val=0; val <= SND_PCM_FORMAT_LAST; ++val)
  26.               std::cout << snd_pcm_format_name((snd_pcm_format_t)val) << " (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;
  27.        std::cout << std::endl;      

2.打開PCM設備和設置參數

  1.  
  2. #include <iostream>
  3. #include <alsa/asoundlib.h>
  4.  
  5. int main()
  6. {
  7.        int                               rc;
  8.        snd_pcm_t*                         handle;
  9.        snd_pcm_hw_params_t*      params;
  10.        unsigned int                  val, val2;
  11.        int                               dir;
  12.        snd_pcm_uframes_t             frames;
  13.  
  14.        if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
  15.        {
  16.               std::cerr << "unable to open pcm devices: " << snd_strerror(rc) << std::endl;
  17.               exit(1);
  18.        }
  19.  
  20.        snd_pcm_hw_params_alloca(&params);
  21.  
  22.        snd_pcm_hw_params_any(handle, params);
  23.  
  24.        snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  25.  
  26.        snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
  27.  
  28.        snd_pcm_hw_params_set_channels(handle, params, 2);
  29.  
  30.        val = 44100;
  31.  
  32.        snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
  33.  
  34.        if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
  35.        {
  36.               std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;
  37.               exit(1);
  38.        }
  39.  
  40.        std::cout << "PCM handle name = " << snd_pcm_name(handle) << std::endl;
  41.  
  42.        std::cout << "PCM state = " << snd_pcm_state_name(snd_pcm_state(handle)) << std::endl;
  43.  
  44.        snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *)&val);
  45.  
  46.        std::cout << "access type = " << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;
  47.  
  48.        snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)(&val));
  49.       
  50.        std::cout << "format = '" << snd_pcm_format_name((snd_pcm_format_t)val) << "' (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;
  51.  
  52.       snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);
  53.       std::cout << "subformat = '" <<
  54.     snd_pcm_subformat_name((snd_pcm_subformat_t)val) << "' (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;
  55.  
  56.       snd_pcm_hw_params_get_channels(params, &val);
  57.       std::cout << "channels = " << val << std::endl;
  58.  
  59.       snd_pcm_hw_params_get_rate(params, &val, &dir);
  60.       std::cout << "rate = " << val << " bps" << std::endl;
  61.  
  62.        snd_pcm_hw_params_get_period_time(params, &val, &dir);
  63.       std::cout << "period time = " << val << " us" << std::endl;
  64.  
  65.       snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  66.       std::cout << "period size = " << static_cast<int>(frames) << " frames" << std::endl;
  67.  
  68.        snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
  69.       std::cout << "buffer time = " << val << " us" << std::endl;
  70.       
  71.        snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &val);
  72.       std::cout << "buffer size = " << val << " frames" << std::endl;
  73.  
  74.       snd_pcm_hw_params_get_periods(params, &val, &dir);
  75.       std::cout << "periods per buffer = " << val << " frames" << std::endl;
  76.  
  77.        snd_pcm_hw_params_get_rate_numden(params, &val, &val2);
  78.       std::cout << "exact rate = " << val/val2 << " bps" << std::endl;
  79.       
  80.       val = snd_pcm_hw_params_get_sbits(params);
  81.       std::cout << "significant bits = " << val << std::endl;
  82.  
  83.       snd_pcm_hw_params_get_tick_time(params, &val, &dir);
  84.       std::cout << "tick time = " << val << " us" << std::endl;
  85.  
  86.       val = snd_pcm_hw_params_is_batch(params);
  87.       std::cout << "is batch = " << val << std::endl;
  88.  
  89.       val = snd_pcm_hw_params_is_block_transfer(params);
  90.       std::cout << "is block transfer = " << val << std::endl;
  91.  
  92.       val = snd_pcm_hw_params_is_double(params);
  93.       std::cout << "is double = " << val << std::endl;
  94.  
  95.        val = snd_pcm_hw_params_is_half_duplex(params);
  96.       std::cout << "is half duplex = " << val << std::endl;
  97.  
  98.        val = snd_pcm_hw_params_is_joint_duplex(params);
  99.       std::cout << "is joint duplex = " << val << std::endl;
  100.  
  101.        val = snd_pcm_hw_params_can_overrange(params);
  102.       std::cout << "can overrange = " << val << std::endl;
  103.  
  104.       val = snd_pcm_hw_params_can_mmap_sample_resolution(params);
  105.       std::cout << "can mmap = " << val << std::endl;
  106.  
  107.       val = snd_pcm_hw_params_can_pause(params);
  108.       std::cout << "can pause = " << val << std::endl;
  109.  
  110.        val = snd_pcm_hw_params_can_resume(params);
  111.       std::cout << "can resume = " << val << std::endl;
  112.  
  113.       val = snd_pcm_hw_params_can_sync_start(params);
  114.       std::cout << "can sync start = " << val << std::endl;
  115.  
  116.       snd_pcm_close(handle);
  117.  
  118.       return 0;
  119. }

 

3.一個簡單的聲音播放程序

 

  1. #include <iostream>
  2. #include <alsa/asoundlib.h>
  3.  
  4. int main()
  5. {
  6.        long                             loops;
  7.        int                               rc;
  8.        int                                       size;
  9.        snd_pcm_t*                         handle;
  10.        snd_pcm_hw_params_t*      params;
  11.        unsigned int                  val;
  12.        int                               dir;
  13.        snd_pcm_uframes_t             frames;
  14.        char*                                  buffer;
  15.  
  16.        if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
  17.        {
  18.               std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;
  19.               exit(1);
  20.        }
  21.  
  22.        snd_pcm_hw_params_alloca(&params);
  23.  
  24.        snd_pcm_hw_params_any(handle, params);
  25.  
  26.        snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  27.  
  28.        snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
  29.  
  30.        snd_pcm_hw_params_set_channels(handle, params, 2);
  31.  
  32.        val = 44100;
  33.  
  34.        snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
  35.  
  36.        frames = 32;
  37.        snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
  38.  
  39.        if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
  40.        {
  41.               std::cerr << "unable to set hw paramseters: " << snd_strerror(rc) << std::endl;
  42.               exit(1);
  43.        }
  44.  
  45.        snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  46.        size = frames * 4;
  47.        buffer = new char[size];
  48.  
  49.        snd_pcm_hw_params_get_period_time(params, &val, &dir);
  50.  
  51.        loops = 5000000 / val;
  52.  
  53.        while (loops > 0) {
  54.               loops--;
  55.               if ( (rc = read(0, buffer, size)) == 0)
  56.               {
  57.                      std::cerr << "end of file on input" << std::endl;
  58.                      break;
  59.               }
  60.               else if (rc != size)
  61.                      std::cerr << "short read: read " << rc << " bytes" << std::endl;
  62.  
  63.               if ( (rc = snd_pcm_writei(handle, buffer, frames)) == -EPIPE)
  64.               {
  65.                      std::cerr << "underrun occurred" << std::endl;
  66.                      snd_pcm_prepare(handle);
  67.               }
  68.               else if (rc < 0)
  69.                      std::cerr << "error from writei: " << snd_strerror(rc) << std::endl;
  70.               else if (rc != (int)frames)
  71.                      std::cerr << "short write, write " << rc << " frames" << std::endl;
  72.        }
  73.  
  74.        snd_pcm_drain(handle);
  75.        snd_pcm_close(handle);
  76.        free(buffer);
  77.  
  78.        return 0;
  79. }

4.一個簡單的記錄聲音的程序

  1.  
  2. #include <iostream>
  3. #include <alsa/asoundlib.h>
  4.  
  5. int main()
  6. {
  7.        long                             loops;
  8.        int                                       rc;
  9.        int                                       size;
  10.        snd_pcm_t*                         handle;
  11.        snd_pcm_hw_params_t*      params;
  12.        unsigned int                  val;
  13.        int                                       dir;
  14.        snd_pcm_uframes_t             frames;
  15.        char*                                  buffer;
  16.  
  17.        if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
  18.        {
  19.               std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;
  20.               exit(1);
  21.        }
  22.  
  23.        snd_pcm_hw_params_alloca(&params);
  24.  
  25.        snd_pcm_hw_params_any(handle, params);
  26.  
  27.        snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  28.  
  29.        snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
  30.  
  31.        snd_pcm_hw_params_set_channels(handle, params, 2);
  32.  
  33.        val = 44100;
  34.        snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
  35.  
  36.        if ( (rc = snd_pcm_hw_params(handle, params)) < 0)
  37.        {
  38.               std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;
  39.               exit(1);
  40.        }
  41.  
  42.        snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  43.  
  44.        size = frames * 4;
  45.        buffer = new char[size];
  46.  
  47.        snd_pcm_hw_params_get_period_time(params, &val, &dir);
  48.  
  49.        loops = 5000000 / val;
  50.  
  51.        while (loops > 0)
  52.        {
  53.               loops --;
  54.               rc = snd_pcm_readi(handle, buffer, frames);
  55.               if (rc == -EPIPE)
  56.               {
  57.                      std::cerr << "overrun occurred" << std::endl;
  58.                      snd_pcm_prepare(handle);
  59.               }
  60.               else if (rc < 0)
  61.                      std::cerr << "error from read: " << snd_strerror(rc) << std::endl;
  62.               else if ( rc != (int)frames)
  63.                      std::cerr << "short read, read " << rc << " frames" << std::endl;
  64.               rc = write(1, buffer, size);
  65.               if (rc != size)
  66.                      std::cerr << "short write: wrote " << rc << " bytes" << std::endl;
  67.        }
  68.  
  69.        snd_pcm_drain(handle);
  70.        snd_pcm_close(handle);
  71.        free(buffer);
  72.       
  73.        return 0;
  74. }

 

編譯的參數:g++ xxx.cpp -o xxx -lasound

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章