當前位置:編程學習大全網 - 編程語言 - 如何修改alsa支持192khz的pcm輸出

如何修改alsa支持192khz的pcm輸出

這裏了解壹下各個參數的含義以及壹些基本概念。

樣本長度(sample):樣本是記錄音頻數據最基本的單位,常見的有8位和16位。

通道數(channel):該參數為1表示單聲道,2則是立體聲。

楨(frame):楨記錄了壹個聲音單元,其長度為樣本長度與通道數的乘積。

采樣率(rate):每秒鐘采樣次數,該次數是針對楨而言。

周期(period):音頻設備壹次處理所需要的楨數,對於音頻設備的數據訪問以及音頻數據的存儲,都是以此為單位。

交錯模式(interleaved):是壹種音頻數據的記錄方式,在交錯模式下,數據以連續楨的形式存放,即首先記錄完楨1的左聲道樣本和右聲道樣本(假設為立體聲格式),再開始楨2的記錄。而在非交錯模式下,首先記錄的是壹個周期內所有楨的左聲道樣本,再記錄右聲道樣本,數據是以連續通道的方式存儲。不過多數情況下,我們只需要使用交錯模式就可以了。

英文原文:/article/6735

period(周期):硬件中中斷間的間隔時間。它表示輸入延時。

聲卡接口中有壹個指針來指示聲卡硬件緩存區中當前的讀寫位置。只要接口在運行,這個指針將循環地指向緩存區中的某個位置。

frame size = sizeof(one sample) * nChannels

alsa中配置的緩存(buffer)和周期(size)大小在runtime中是以幀(frames)形式存儲的。

period_bytes = frames_to_bytes(runtime, runtime->period_size);

bytes_to_frames()

The period and buffer sizes are not dependent on the sample format because they are measured in frames; you do not need to change them.

ALSA聲音編程介紹

ALSA表示高級Linux聲音體系結構(Advanced Linux Sound Architecture)。它由壹系列內核驅動,應用程序編譯接口(API)以及支持Linux下聲音的實用程序組成。這篇文章裏,我將簡單介紹ALSA項目的基本框架以及它的軟件組成。主要集中介紹PCM接口編程,包括您可以自動實踐的程序示例。

您使用ALSA的原因可能就是因為它很新,但它並不是唯壹可用的聲音API。如果您想完成低級的聲音操作,以便能夠最大化地控制聲音並最大化地提高性能,或者如果您使用其它聲音API沒有的特性,那麽ALSA是很好的選擇。如果您已經寫了壹個音頻程序,妳可能想要為ALSA聲卡驅動添加本地支持。如果您對音頻不感興趣,只是想播放音頻文件,那麽高級的API將是更好的選擇,比如SDL,OpenAL以及那些桌面環境提供的工具集。另外,您只能在有ALSA支持的Linux環境中使用ALSA。

ALSA歷史

ALSA項目發起的起因是Linux下的聲卡驅動(OSS/Free drivers)沒有得到積極的維護。並且落後於新的聲卡技術。Jaroslav Kysela早先寫了壹個聲卡驅動,並由此開始了ALSA項目,隨便,更多的開發者加入到開發隊伍中,更多的聲卡得到支持,API的結構也得到了重組。

Linux內核2.5在開發過程中,ALSA被合並到了官方的源碼樹中。在發布內核2.6後,ALSA已經內建在穩定的內核版本中並將廣泛地使用。

數字音頻基礎

聲音由變化的氣壓組成。它被麥克風這樣的轉換器轉換成電子形式。模/數(ADC)轉換器將模擬電壓轉換成離散的樣本值。聲音以固定的時間間隔被采樣,采樣的速率稱為采樣率。把樣本輸出到數/模(DAC)轉換器,比如擴音器,最後轉換成原來的模擬信號。

樣本大小以位來表示。樣本大小是影響聲音被轉換成數字信號的精確程度的因素之壹。另壹個主要的因素是采樣率。奈奎斯特(Nyquist)理論中,只要離散系統的奈奎斯特頻率高於采樣信號的最高頻率或帶寬,就可以避免混疊現象。

ALSA基礎

ALSA由許多聲卡的聲卡驅動程序組成,同時它也提供壹個稱為libasound的API庫。應用程序開發者應該使用libasound而不是內核中的ALSA接口。因為libasound提供最高級並且編程方便的編程接口。並且提供壹個設備邏輯命名功能,這樣開發者甚至不需要知道類似設備文件這樣的低層接口。相反,OSS/Free驅動是在內核系統調用級上編程,它要求開發者提供設備文件名並且利用ioctrl來實現相應的功能。為了向後兼容,ALSA提供內核模塊來模擬OSS,這樣之前的許多在OSS基礎上開發的應用程序不需要任何改動就可以在ALSA上運行。另外,libaoss庫也可以模擬OSS,而它不需要內核模塊。

ALSA包含插件功能,使用插件可以擴展新的聲卡驅動,包括完全用軟件實現的虛擬聲卡。ALSA提供壹系列基於命令行的工具集,比如混音器(mixer),音頻文件播放器(aplay),以及控制特定聲卡特定屬性的工具。

ALSA體系結構

ALSA API可以分解成以下幾個主要的接口:

1 控制接口:提供管理聲卡註冊和請求可用設備的通用功能

2 PCM接口:管理數字音頻回放(playback)和錄音(capture)的接口。本文後續總結重點放在這個接口上,因為它是開發數字音頻程序最常用到的接口。

3 Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),標準的電子樂器。這些API提供對聲卡上MIDI總線的訪問。這個原始接口基於MIDI事件工作,由程序員負責管理協議以及時間處理。

4 定時器(Timer)接口:為同步音頻事件提供對聲卡上時間處理硬件的訪問。

5 時序器(Sequencer)接口

6 混音器(Mixer)接口

設備命名

API庫使用邏輯設備名而不是設備文件。設備名字可以是真實的硬件名字也可以是插件名字。硬件名字使用hw:i,j這樣的格式。其中i是卡號,j是這塊聲卡上的設備號。第壹個聲音設備是hw:0,0.這個別名默認引用第壹塊聲音設備並且在本文示例中壹真會被用到。插件使用另外的唯壹名字。比如plughw:,表示壹個插件,這個插件不提供對硬件設備的訪問,而是提供像采樣率轉換這樣的軟件特性,硬件本身並不支持這樣的特性。

聲音緩存和數據傳輸

每個聲卡都有壹個硬件緩存區來保存記錄下來的樣本。當緩存區足夠滿時,聲卡將產生壹個中斷。內核聲卡驅動然後使用直接內存(DMA)訪問通道將樣本傳送到內存中的應用程序緩存區。類似地,對於回放,任何應用程序使用DMA將自己的緩存區數據傳送到聲卡的硬件緩存區中。

這樣硬件緩存區是環緩存。也就是說當數據到達緩存區末尾時將重新回到緩存區的起始位置。ALSA維護壹個指針來指向硬件緩存以及應用程序緩存區中數據操作的當前位置。從內核外部看,我們只對應用程序的緩存區感興趣,所以本文只討論應用程序緩存區。

應用程序緩存區的大小可以通過ALSA庫函數調用來控制。緩存區可以很大,壹次傳輸操作可能會導致不可接受的延遲,我們把它稱為延時(latency)。為了解決這個問題,ALSA將緩存區拆分成壹系列周期(period)(OSS/Free中叫片斷fragments).ALSA以period為單元來傳送數據。

壹個周期(period)存儲壹些幀(frames)。每壹幀包含時間上壹個點所抓取的樣本。對於立體聲設備,壹個幀會包含兩個信道上的樣本。圖1展示了分解過程:壹個緩存區分解成周期,然後是幀,然後是樣本。圖中包含壹些假定的數值。圖中左右信道信息被交替地存儲在壹個幀內。這稱為交錯(interleaved)模式。在非交錯模式中,壹個信道的所有樣本數據存儲在另外壹個信道的數據之後。

Over and Under Run

當壹個聲卡活動時,數據總是連續地在硬件緩存區和應用程序緩存區間傳輸。但是也有例外。在錄音例子中,如果應用程序讀取數據不夠快,循環緩存區將會被新的數據覆蓋。這種數據的丟失被稱為overrun.在回放例子中,如果應用程序寫入數據到緩存區中的速度不夠快,緩存區將會"餓死"。這樣的錯誤被稱為"underrun"。在ALSA文檔中,有時將這兩種情形統稱為"XRUN"。適當地設計應用程序可以最小化XRUN並且可以從中恢復過來。

壹個典型的聲音程序

使用PCM的程序通常類似下面的偽代碼:

打開回放或錄音接口

設置硬件參數(訪問模式,數據格式,信道數,采樣率,等等)

while 有數據要被處理:

讀PCM數據(錄音)

或 寫PCM數據(回放)

關閉接口

我們將在下文中看到壹些可以工作的代碼。我建議您在妳的Linux系統上測試運行這些代碼。查看輸出並嘗試修改推薦的代碼。和本文相關的所有實例清單可以從FTP中獲取:ftp.ssc.com/pub/lj/listings/issue126/6735.tgz。

Listing 1. Display Some PCM Types and Formats

#include <alsa/asoundlib.h>

int main() {

int val;

printf("ALSA library version: %s/n",

SND_LIB_VERSION_STR);

printf("/nPCM stream types:/n");

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

printf(" %s/n",

snd_pcm_stream_name((snd_pcm_stream_t)val));

printf("/nPCM access types:/n");

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

printf(" %s/n",

snd_pcm_access_name((snd_pcm_access_t)val));

printf("/nPCM formats:/n");

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

if (snd_pcm_format_name((snd_pcm_format_t)val)

!= NULL)

printf(" %s (%s)/n",

snd_pcm_format_name((snd_pcm_format_t)val),

snd_pcm_format_description(

(snd_pcm_format_t)val));

printf("/nPCM subformats:/n");

for (val = 0; val <= SND_PCM_SUBFORMAT_LAST;

val++)

printf(" %s (%s)/n",

snd_pcm_subformat_name((

snd_pcm_subformat_t)val),

snd_pcm_subformat_description((

snd_pcm_subformat_t)val));

printf("/nPCM states:/n");

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

printf(" %s/n",

snd_pcm_state_name((snd_pcm_state_t)val));

return 0;

}

清單壹顯示了壹些ALSA使用的PCM數據類型和參數。首先需要做的是包括頭文件。這些頭文件包含了所有庫函數的聲明。其中之壹就是顯示ALSA庫的版本。

這個程序剩下的部分的叠代壹些PCM數據類型,以流類型開始。ALSA為每次叠代的最後值提供符號常量名,並且提供功能函數以顯示某個特定值的描述字符串。妳將會看到,ALSA支持許多格式,在我的1.0.15版本裏,支持多達36種格式。

這個程序必須鏈接到alsalib庫,通過在編譯時需要加上-lasound選項。有些alsa庫函數使用dlopen函數以及浮點操作,所以您可能還需要加上-ldl,-lm選項。

下面是該程序的Makefile:

CC=gcc

TARGET=test

SRC=$(wildcard *.c)

OBJECT= ${SRC:.c=.o}

INCLUDES=-I/usr/include/alsa

LDFLAGS=-lasound

all:$(TARGET)

$(OBJECT):$(SRC)

$(CC) -c $(INCLUDES) $<

$(TARGET):$(OBJECT)

$(CC) -o $@ $< $(LDFLAGS)

.PHONY:clean

clean:

@rm -rf $(OBJECT) $(TARGET) *~

Listing 2. Opening PCM Device and Setting Parameters

/*

This example opens the default PCM device, sets

some parameters, and then displays the value

of most of the hardware parameters. It does not

perform any sound playback or recording.

*/

/* Use the newer ALSA API */

#define ALSA_PCM_NEW_HW_PARAMS_API

/* All of the ALSA library API is defined

* in this header */

#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;

/* Open PCM device for playback. */

rc = snd_pcm_open(&handle, "default",

SND_PCM_STREAM_PLAYBACK, 0);

if (rc < 0) {

fprintf(stderr,

"unable to open pcm device: %s/n",

snd_strerror(rc));

exit(1);

}

/* Allocate a hardware parameters object. */

snd_pcm_hw_params_alloca(&params);

/* Fill it in with default values. */

snd_pcm_hw_params_any(handle, params);

/* Set the desired hardware parameters. */

/* Interleaved mode */

snd_pcm_hw_params_set_access(handle, params,

SND_PCM_ACCESS_RW_INTERLEAVED);

/* Signed 16-bit little-endian format */

snd_pcm_hw_params_set_format(handle, params,

SND_PCM_FORMAT_S16_LE);

/* Two channels (stereo) */

snd_pcm_hw_params_set_channels(handle, params, 2);

/* 44100 bits/second sampling rate (CD quality) */

val = 44100;

snd_pcm_hw_params_set_rate_near(handle,

params, &val, &dir);

/* Write the parameters to the driver */

rc = snd_pcm_hw_params(handle, params);

if (rc < 0) {

fprintf(stderr,

"unable to set hw parameters: %s/n",

snd_strerror(rc));

exit(1);

}

/* Display information about the PCM interface */

printf("PCM handle name = '%s'/n",

snd_pcm_name(handle));

printf("PCM state = %s/n",

snd_pcm_state_name(snd_pcm_state(handle)));

snd_pcm_hw_params_get_access(params,

(snd_pcm_access_t *) &val);

printf("access type = %s/n",

snd_pcm_access_name((snd_pcm_access_t)val));

snd_pcm_hw_params_get_format(params, &val);

printf("format = '%s' (%s)/n",

snd_pcm_format_name((snd_pcm_format_t)val),

snd_pcm_format_description(

(snd_pcm_format_t)val));

snd_pcm_hw_params_get_subformat(params,

(snd_pcm_subformat_t *)&val);

printf("subformat = '%s' (%s)/n",

snd_pcm_subformat_name((snd_pcm_subformat_t)val),

snd_pcm_subformat_description(

(snd_pcm_subformat_t)val));

snd_pcm_hw_params_get_channels(params, &val);

printf("channels = %d/n", val);

snd_pcm_hw_params_get_rate(params, &val, &dir);

printf("rate = %d bps/n", val);

snd_pcm_hw_params_get_period_time(params,

&val, &dir);

printf("period time = %d us/n", val);

snd_pcm_hw_params_get_period_size(params,

&frames, &dir);

printf("period size = %d frames/n", (int)frames);

snd_pcm_hw_params_get_buffer_time(params,

&val, &dir);

printf("buffer time = %d us/n", val);

snd_pcm_hw_params_get_buffer_size(params,

(snd_pcm_uframes_t *) &val);

printf("buffer size = %d frames/n", val);

snd_pcm_hw_params_get_periods(params, &val, &dir);

printf("periods per buffer = %d frames/n", val);

snd_pcm_hw_params_get_rate_numden(params,

&val, &val2);

printf("exact rate = %d/%d bps/n", val, val2);

val = snd_pcm_hw_params_get_sbits(params);

printf("significant bits = %d/n", val);

snd_pcm_hw_params_get_tick_time(params,

&val, &dir);

printf("tick time = %d us/n", val);

val = snd_pcm_hw_params_is_batch(params);

printf("is batch = %d/n", val);

val = snd_pcm_hw_params_is_block_transfer(params);

printf("is block transfer = %d/n", val);

val = snd_pcm_hw_params_is_double(params);

printf("is double = %d/n", val);

val = snd_pcm_hw_params_is_half_duplex(params);

printf("is half duplex = %d/n", val);

val = snd_pcm_hw_params_is_joint_duplex(params);

printf("is joint duplex = %d/n", val);

val = snd_pcm_hw_params_can_overrange(params);

printf("can overrange = %d/n", val);

val = snd_pcm_hw_params_can_mmap_sample_resolution(params);

printf("can mmap = %d/n", val);

val = snd_pcm_hw_params_can_pause(params);

printf("can pause = %d/n", val);

val = snd_pcm_hw_params_can_resume(params);

printf("can resume = %d/n", val);

val = snd_pcm_hw_params_can_sync_start(params);

printf("can sync start = %d/n", val);

snd_pcm_close(handle);

return 0;

}

  • 上一篇:微信公眾號運營哪家好?
  • 下一篇:2021天津各大體育館收費標準天津體育館開放時間及優惠政策
  • copyright 2024編程學習大全網