1. 程式人生 > >ALSA架構簡介和ALSA學習

ALSA架構簡介和ALSA學習

ALSA Advanced Linux Sound Architecture(高階Linux聲音體系)的縮寫)是為音效卡提供驅動的Linux核心元件,以替代原先的OSS(開放聲音系統)。ALSA除了像OSS那樣提供一組核心驅動程式模組以外,還專門為簡化應用程式的編寫提供了相應的庫函式,與OSS提供的基於ioctl的原始程式設計介面相比,ALSA函式庫使用起來要更加方便一點。

ALSA的主要特點如下:

支援多種音效卡裝置。

模組化的核心驅動程式。

支援SMP和多執行緒。

提供應用開發函式庫以簡化應用程式開發。

支援OSS API,相容OSS應用程式。

ALSA具有更加友好的程式設計介面,並且完全兼容於

OSS,對應用程式來講無疑是一個更佳地選擇。ALSA系統包括驅動包alsa-driver,開發包alsa-libs,開發包外掛alsa-libplugins,設定管理工具包alsa-utils,其他聲音相關處理小程式包alsa-tools,特殊音訊韌體支援包alsa-firmwareOSS介面相容模擬層工具alsa-oss7個子專案,其中只有驅動包是必須的。

alsa-driver指核心驅動程式,包括硬體相關的程式碼和一些公共程式碼,非常龐大。alsa-libs指使用者空間的函式庫,提供給應用程式使用,應用程式應包括標頭檔案asoundlib.h。並使用共享庫libasound.so

alsa-utils

包含一些基於ALSA的用於控制音效卡的應用程式,如alsaconf(偵測系統中音效卡並寫一個適合的ALSA配置檔案),aplay(基於命令列的聲音檔案播放),arecord(基於命令列的聲音檔案錄製)等。

目前ALSA核心提供給使用者空間的介面有:

資訊介面(proc/asound

控制介面(dev/snd/controlCX

混音器介面(dev/snd/mixerCXDX

PCM介面(dev/snd/pcmCXDX

Raw迷笛介面(dev/snd/midiCXDX

音序器介面(dev/snd/seq

定時器介面(dev/snd/timer

OSS類似,上述介面也以檔案的方式被提供,不同的是這些介面被提供給

alsa-lib使用,而不是直接給應用程式使用的。應用程式最好使用alsa-lib,或者更高階的介面。

下圖所示為ALSA音效卡驅動與使用者空間體系結構的簡圖,從中可以看出ALSA核心驅動與使用者空間庫及OSS之間的關係

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

. 介紹

      ALSA 標準是一個先進的 linux 聲音體系。它包含核心驅動集合, API 庫和工具對 Linux 聲音進行支援。 ALSA 包含一系列核心驅動對不同的音效卡進行支援,還提供了 libasound API 庫。用這些進行寫程式不需要開啟裝置等操作,所以程式設計人員在寫程式的時候不會被底層的東西困擾。與此相反 OSS/Free 驅動在核心層次呼叫,需要指定裝置名和呼叫 ioctl 。為提供向後相容, ALSA 提供核心模組模仿 OSS/Free 驅動,所以大多數的程式不需要改動。 ALSA 擁有呼叫外掛的能力對新裝置提供擴充套件,包括那些用軟體模擬出來的虛擬裝置。 ALSA 還提供一組命令列工具包括   mixer, sound file player 和工具控制一些特別的音效卡的特別的作用。

.ALSA 體系:

ALSA API 被主要分為以下幾種介面:

l控制介面:提供靈活的方式管理註冊的音效卡和對存在的音效卡進行查詢。

lPCM 介面:提供管理數字音訊的捕捉和回放。

l原始 MIDI 介面 : 支援 MIDI (Musical Instrument Digital Interface), 一種標準電子音樂指令集。 這些 API 提供訪問音效卡上的 MIDI 匯流排。這些原始藉口直接工作在 The  MIDI 事件上,程式設計師只需要管理協議和時間。

l記時介面 : 為支援聲音的同步事件提供訪問音效卡上的定時器。

l音序器介面:一個比原始 MIDI 介面高階的 MIDI 程式設計和聲音同步高層介面。它可以處理很多的 MIDI 協議和定時器。

l混音器介面:控制傳送訊號和控制聲音大小的音效卡上的裝置。

. 音效卡的快取和資料的傳輸:

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

音效卡的快取是環狀的,這裡只討論應用程式中的記憶體結構: ALSA 將資料分成連續的片段然後傳到按單元片段傳輸。

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

        open interface for capture or playback

        set hardware parameters

        (access mode, data format, channels, rate, etc.)

        while there is data to be processed:

        read PCM data (capture)

         or write PCM data (playback)

        close interface

. 一些例子:

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

#include <iostream>

#include <alsa/asoundlib.h>

int main()

{

       std::cout << "ALSA library version: " << SND_LIB_VERSION_STR << std::endl;

       std::cout << "PCM stream types: " << std::endl;

       for (int val=0; val <= SND_PCM_STREAM_LAST; ++val)

              std::cout << snd_pcm_stream_name((snd_pcm_stream_t)val) << std::endl;

       std::cout << std::endl;

       std::cout << "PCM access types: " << std::endl;

       for (int val=0; val <= SND_PCM_ACCESS_LAST; ++val)

              std::cout << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;

       std::cout << std::endl;

       std::cout << "PCM subformats: " << std::endl;

       for (int val=0; val <= SND_PCM_SUBFORMAT_LAST; ++val)

              std::cout << snd_pcm_subformat_name((snd_pcm_subformat_t)val) << " (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;

       std::cout << std::endl;

       std::cout << "PCM states: " << std::endl;

       for (int val=0; val <= SND_PCM_STATE_LAST; ++val)

              std::cout << snd_pcm_state_name((snd_pcm_state_t)val) << std::endl;

       std::cout << std::endl;

       std::cout << "PCM formats: " << std::endl;

       for (int val=0; val <= SND_PCM_FORMAT_LAST; ++val)

              std::cout << snd_pcm_format_name((snd_pcm_format_t)val) << " (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;

       std::cout << std::endl;

}

2. 開啟 PCM 裝置和設定引數

#include <iostream>

#include <alsa/asoundlib.h>

int main()

{

       int                               rc;

       snd_pcm_t*                         handle;

       snd_pcm_hw_params_t*      params;

       unsigned int                  val, val2;

       int                               dir;

       snd_pcm_uframes_t             frames;

       if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)

       {

              std::cerr << "unable to open pcm devices: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       snd_pcm_hw_params_alloca(&params);

       snd_pcm_hw_params_any(handle, params);

       snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

       snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

       snd_pcm_hw_params_set_channels(handle, params, 2);

       val = 44100;

       snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

       if ( (rc = snd_pcm_hw_params(handle, params)) < 0)

       {

              std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       std::cout << "PCM handle name = " << snd_pcm_name(handle) << std::endl;

       std::cout << "PCM state = " << snd_pcm_state_name(snd_pcm_state(handle)) << std::endl;

       snd_pcm_hw_params_get_access(params, (snd_pcm_access_t *)&val);

       std::cout << "access type = " << snd_pcm_access_name((snd_pcm_access_t)val) << std::endl;

       snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)(&val));

       std::cout << "format = '" << snd_pcm_format_name((snd_pcm_format_t)val) << "' (" << snd_pcm_format_description((snd_pcm_format_t)val) << ")" << std::endl;

      snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t *)&val);

      std::cout << "subformat = '" <<

    snd_pcm_subformat_name((snd_pcm_subformat_t)val) << "' (" << snd_pcm_subformat_description((snd_pcm_subformat_t)val) << ")" << std::endl;

      snd_pcm_hw_params_get_channels(params, &val);

      std::cout << "channels = " << val << std::endl;

       snd_pcm_hw_params_get_rate(params, &val, &dir);

      std::cout << "rate = " << val << " bps" << std::endl;

       snd_pcm_hw_params_get_period_time(params, &val, &dir);

      std::cout << "period time = " << val << " us" << std::endl;

      snd_pcm_hw_params_get_period_size(params, &frames, &dir);

      std::cout << "period size = " << static_cast<int>(frames) << " frames" << std::endl;

       snd_pcm_hw_params_get_buffer_time(params, &val, &dir);

      std::cout << "buffer time = " << val << " us" << std::endl;

       snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &val);

      std::cout << "buffer size = " << val << " frames" << std::endl;

      snd_pcm_hw_params_get_periods(params, &val, &dir);

      std::cout << "periods per buffer = " << val << " frames" << std::endl;

       snd_pcm_hw_params_get_rate_numden(params, &val, &val2);

      std::cout << "exact rate = " << val/val2 << " bps" << std::endl;

      val = snd_pcm_hw_params_get_sbits(params);

      std::cout << "significant bits = " << val << std::endl;

      snd_pcm_hw_params_get_tick_time(params, &val, &dir);

      std::cout << "tick time = " << val << " us" << std::endl;

      val = snd_pcm_hw_params_is_batch(params);

      std::cout << "is batch = " << val << std::endl;

      val = snd_pcm_hw_params_is_block_transfer(params);

      std::cout << "is block transfer = " << val << std::endl;

      val = snd_pcm_hw_params_is_double(params);

      std::cout << "is double = " << val << std::endl;

       val = snd_pcm_hw_params_is_half_duplex(params);

      std::cout << "is half duplex = " << val << std::endl;

       val = snd_pcm_hw_params_is_joint_duplex(params);

      std::cout << "is joint duplex = " << val << std::endl;

       val = snd_pcm_hw_params_can_overrange(params);

      std::cout << "can overrange = " << val << std::endl;

      val = snd_pcm_hw_params_can_mmap_sample_resolution(params);

      std::cout << "can mmap = " << val << std::endl;

      val = snd_pcm_hw_params_can_pause(params);

      std::cout << "can pause = " << val << std::endl;

       val = snd_pcm_hw_params_can_resume(params);

      std::cout << "can resume = " << val << std::endl;

      val = snd_pcm_hw_params_can_sync_start(params);

      std::cout << "can sync start = " << val << std::endl;

       snd_pcm_close(handle);

      return 0;

}

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

#include <iostream>

#include <alsa/asoundlib.h>

int main()

{

       long                             loops;

       int                               rc;

       int                                       size;

       snd_pcm_t*                         handle;

       snd_pcm_hw_params_t*      params;

       unsigned int                  val;

       int                               dir;

       snd_pcm_uframes_t             frames;

       char*                                  buffer;

       if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)

       {

              std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       snd_pcm_hw_params_alloca(&params);

       snd_pcm_hw_params_any(handle, params);

       snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

       snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

       snd_pcm_hw_params_set_channels(handle, params, 2);

       val = 44100;

       snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

       frames = 32;

       snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

       if ( (rc = snd_pcm_hw_params(handle, params)) < 0)

       {

              std::cerr << "unable to set hw paramseters: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       snd_pcm_hw_params_get_period_size(params, &frames, &dir);

       size = frames * 4;

       buffer = new char[size];

       snd_pcm_hw_params_get_period_time(params, &val, &dir);

       loops = 5000000 / val;

       while (loops > 0) {

              loops--;

              if ( (rc = read(0, buffer, size)) == 0)

              {

                     std::cerr << "end of file on input" << std::endl;

                     break;

              }

              else if (rc != size)

                     std::cerr << "short read: read " << rc << " bytes" << std::endl;

              if ( (rc = snd_pcm_writei(handle, buffer, frames)) == -EPIPE)

              {

                     std::cerr << "underrun occurred" << std::endl;

                     snd_pcm_prepare(handle);

              }

              else if (rc < 0)

                     std::cerr << "error from writei: " << snd_strerror(rc) << std::endl;

              else if (rc != (int)frames)

                     std::cerr << "short write, write " << rc << " frames" << std::endl;

       }

       snd_pcm_drain(handle);

       snd_pcm_close(handle);

       free(buffer);

       return 0;

}

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

#include <iostream>

#include <alsa/asoundlib.h>

int main()

{

       long                             loops;

       int                                       rc;

       int                                       size;

       snd_pcm_t*                         handle;

       snd_pcm_hw_params_t*      params;

       unsigned int                  val;

       int                                       dir;

       snd_pcm_uframes_t             frames;

       char*                                  buffer;

       if ( (rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)

       {

              std::cerr << "unable to open pcm device: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       snd_pcm_hw_params_alloca(&params);

       snd_pcm_hw_params_any(handle, params);

       snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

       snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

       snd_pcm_hw_params_set_channels(handle, params, 2);

       val = 44100;

       snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

       if ( (rc = snd_pcm_hw_params(handle, params)) < 0)

       {

              std::cerr << "unable to set hw parameters: " << snd_strerror(rc) << std::endl;

              exit(1);

       }

       snd_pcm_hw_params_get_period_size(params, &frames, &dir);

       size = frames * 4;

       buffer = new char[size];

       snd_pcm_hw_params_get_period_time(params, &val, &dir);

       loops = 5000000 / val;

       while (loops > 0)

       {

              loops --;

              rc = snd_pcm_readi(handle, buffer, frames);

              if (rc == -EPIPE)

              {

                     std::cerr << "overrun occurred" << std::endl;

                     snd_pcm_prepare(handle);

              }

              else if (rc < 0)

                     std::cerr << "error from read: " << snd_strerror(rc) << std::endl;

              else if ( rc != (int)frames)

                     std::cerr << "short read, read " << rc << " frames" << std::endl;

              rc = write(1, buffer, size);

              if (rc != size)

                     std::cerr << "short write: wrote " << rc << " bytes" << std::endl;

       }

       snd_pcm_drain(handle);

       snd_pcm_close(handle);

       free(buffer);

       return 0;

}