ALSA架構簡介和ALSA學習
ALSA (Advanced Linux Sound Architecture(高階Linux聲音體系)的縮寫)是為音效卡提供驅動的Linux核心元件,以替代原先的OSS(開放聲音系統)。ALSA除了像OSS那樣提供一組核心驅動程式模組以外,還專門為簡化應用程式的編寫提供了相應的庫函式,與OSS提供的基於ioctl的原始程式設計介面相比,ALSA函式庫使用起來要更加方便一點。
ALSA的主要特點如下:
支援多種音效卡裝置。
模組化的核心驅動程式。
支援SMP和多執行緒。
提供應用開發函式庫以簡化應用程式開發。
支援OSS API,相容OSS應用程式。
ALSA具有更加友好的程式設計介面,並且完全兼容於
alsa-driver指核心驅動程式,包括硬體相關的程式碼和一些公共程式碼,非常龐大。alsa-libs指使用者空間的函式庫,提供給應用程式使用,應用程式應包括標頭檔案asoundlib.h。並使用共享庫libasound.so。
alsa-utils
目前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音效卡驅動與使用者空間體系結構的簡圖,從中可以看出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(¶ms);
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(¶ms);
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(¶ms);
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;
}