日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

alsa声音编程介绍

發布時間:2023/12/10 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 alsa声音编程介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://blog.csdn.net/q553716434/article/details/7881552

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

清單2打開默認的PCM設備,設置一些硬件參數并且打印出最常用的硬件參數值。它并不做任何回放或錄音的操作。snd_pcm_open打開默認的PCM設備并設置訪問模式為PLAYBACK。這個函數返回一個句柄,這個句柄保存在第一個函數參數中。該句柄會在隨后的函數中用到。像其它函數一樣,這個函數返回一個整數。如果返回值小于0,則代碼函數調用出錯。如果出錯,我們用snd_errstr打開錯誤信息并退出。
為了設置音頻流的硬件參數,我們需要分配一個類型為snd_pcm_hw_param的變量。分配用到函數宏snd_pcm_hw_params_alloca。下一步,我們使用函數snd_pcm_hw_params_any來初始化這個變量,傳遞先前打開的PCM流句柄。
接下來,我們調用API來設置我們所需的硬件參數。這些函數需要三個參數:PCM流句柄,參數類型,參數值。我們設置流為交錯模式,16位的樣本大小,2個信道,44100bps的采樣率。對于采樣率而言,聲音硬件并不一定就精確地支持我們所定的采樣率,但是我們可以使用函數snd_pcm_hw_params_set_rate_near來設置最接近我們指定的采樣率的采樣率。其實只有當我們調用函數snd_pcm_hw_params后,硬件參數才會起作用。
程序的剩余部分獲得并打印一些PCM流參數,包括周期和緩沖區大小。結果可能會因為聲音硬件的不同而不同。
運行該程序后,做實驗,改動一些代碼。把設備名字改成hw:0,0,然后看結果是否會有變化。設置不同的硬件參數然后觀察結果的變化。

Listing 3. Simple Sound Playback

/*This example reads standard from input and writes to the default PCM device for 5 seconds of data.*//* Use the newer ALSA API */ #define ALSA_PCM_NEW_HW_PARAMS_API#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;/* 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);/* Set period size to 32 frames. */frames = 32;snd_pcm_hw_params_set_period_size_near(handle,params, &frames, &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);}/* Use a buffer large enough to hold one period */snd_pcm_hw_params_get_period_size(params, &frames,&dir);size = frames * 4; /* 2 bytes/sample, 2 channels */buffer = (char *) malloc(size);/* We want to loop for 5 seconds */snd_pcm_hw_params_get_period_time(params,&val, &dir);/* 5 seconds in microseconds divided by* period time */loops = 5000000 / val;while (loops > 0) {loops--;rc = read(0, buffer, size);if (rc == 0) {fprintf(stderr, "end of file on input\n");break;} else if (rc != size) {fprintf(stderr,"short read: read %d bytes\n", rc);}rc = snd_pcm_writei(handle, buffer, frames);if (rc == -EPIPE) {/* EPIPE means underrun */fprintf(stderr, "underrun occurred\n");snd_pcm_prepare(handle);} else if (rc < 0) {fprintf(stderr,"error from writei: %s\n",snd_strerror(rc));} else if (rc != (int)frames) {fprintf(stderr,"short write, write %d frames\n", rc);}}snd_pcm_drain(handle);snd_pcm_close(handle);free(buffer);return 0; }清單3擴展了之前的示例。向聲卡中寫入了一些聲音樣本以實現聲音回放。在這個例子中,我們從標準輸入中讀取數據,每個周期讀取足夠多的數據,然后將它們寫入到聲卡中,直到5秒鐘的數據全部傳輸完畢。 這個程序的開始處和之前的版本一樣---打開PCM設備、設置硬件參數。我們使用由ALSA自己選擇的周期大小,申請該大小的緩沖區來存儲樣本。然后我們找出周期時間,這樣我們就能計算出本程序為了能夠播放5秒鐘,需要多少個周期。 在處理數據的循環中,我們從標準輸入中讀入數據,并往緩沖區中填充一個周期的樣本。然后檢查并處理錯誤,這些錯誤可能是由到達文件結尾,或讀取的數據長度與我期望的數據長度不一致導致的。 我們調用snd_pcm_writei來發送數據。它操作起來很像內核的寫系統調用,只是這里的大小參數是以幀來計算的。我們檢查其返回代碼值。返回值為EPIPE表明發生了underrun,使得PCM音頻流進入到XRUN狀態并停止處理數據。從該狀態中恢復過來的標準方法是調用snd_pcm_prepare函數,把PCM流置于PREPARED狀態,這樣下次我們向該PCM流中數據時,它就能重新開始處理數據。如果我們得到的錯誤碼不是EPIPE,我們把錯誤碼打印出來,然后繼續。最后,如果寫入的幀數不是我們期望的,則打印出錯誤消息。 這個程序一直循環,直到5秒鐘的幀全部傳輸完,或者輸入流讀到文件結尾。然后我們調用snd_pcm_drain把所有掛起沒有傳輸完的聲音樣本傳輸完全,最后關閉該音頻流,釋放之前動態分配的緩沖區,退出。 我們可以看到這個程序沒有什么用,除非標準輸入被重定向到了其它其它的文件。嘗試用設備/dev/urandom來運行這個程序,該設備產生隨機數據:./example3 </dev/urandom

隨機數據會產生5秒鐘的白色噪聲。
然后,嘗試把標準輸入重定向到設備/dev/null和/dev/zero上,并比較結果。改變一些參數,例如采樣率和數據格式,然后查看結果的變化。

Listing 4. Simple Sound Recording


/*

This example reads from the default PCM device
and writes to standard output for 5 seconds of data.

*/

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API

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

/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default",
??????????????????? SND_PCM_STREAM_CAPTURE, 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);

/* Set period size to 32 frames. */
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle,
????????????????????????????? params, &frames, &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);
}

/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params,
????????????????????????????????????? &frames, &dir);
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);

/* We want to loop for 5 seconds */
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) {
????? /* EPIPE means overrun */
????? fprintf(stderr, "overrun occurred\n");
????? snd_pcm_prepare(handle);
??? } else if (rc < 0) {
????? fprintf(stderr,
????????????? "error from read: %s\n",
????????????? snd_strerror(rc));
??? } else if (rc != (int)frames) {
????? fprintf(stderr, "short read, read %d frames\n", rc);
??? }
??? rc = write(1, buffer, size);
??? if (rc != size)
????? fprintf(stderr,
????????????? "short write: wrote %d bytes\n", rc);
}

snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);

return 0;
}

清單4類似于清單3中的程序,除了這里的程序時做聲音的抓取(錄音)。當打開PCM設備時我們指定打開模式為SND_PCM_STREAM_CPATURE。在主循環中,我們調用snd_pcm_readi從聲卡中讀取數據,并把它們寫入到標準輸出。同樣地,我們檢查是否有overrun,如果存在,用與前例中相同的方式處理。
運行清單4的程序將錄制將近5秒鐘的聲音數據,并把它們發送到標準輸出。你也可以重定向到某個文件。如果你有一個麥克風連接到你的聲卡,可以使用某個混音程序(mixer)設置錄音源和級別。同樣地,你也可以運行一個CD播放器程序并把錄音源設成CD。嘗試運行程序4并把輸出定向到某個文件,然后運行程序3播放該文件里的聲音數據:
./listing4 > sound.raw
./listing3 < sound.raw
如果你的聲卡支持全雙工,你可以通過管道把兩個程序連接起來,這樣就可以從聲卡中聽到錄制的聲音:
./listing4 | ./listing3
同樣地,您可以做實驗,看看采樣率和樣本格式的變化會產生什么影響。

高級特性
在前面的例子中,PCM流是以阻塞模式操作的,也就是說,直到數據已經傳送完,PCM接口調用才會返回。在事件驅動的交互式程序中,這樣會長時間阻塞應用程序,通常是不能接受的。ALSA支持以非阻塞模式打開音頻流,這樣讀寫函數調用后立即返回。如果數據傳輸被掛起,調用不能被處理,ALSA就是返回一個EBUSY的錯誤碼。
許多圖形應用程序使用回調來處理事件。ALSA支持以異步的方式打開一個PCM音頻流。這使得當某個周期的樣本數據被傳輸完后,某個已注冊的回調函數將會調用。

這里用到的snd_pcm_readi和snd_pcm_writei調用和Linux下的讀寫系統調用類似。字母i表示處理的幀是交錯式(interleaved)的。ALSA中存在非交互模式的對應的函數。Linux下的許多設備也支持mmap系統調用,這個調用將設備內存映射到主內存,這樣數據就可以用指針來維護。ALSA也運行以mmap模式打開一個PCM信道,這允許有效的零拷貝(zero copy)方式訪問聲音數據。

總結
我希望這篇文章能夠激勵你嘗試編寫某些ALSA程序。伴隨著2.6內核在Linux發布版本(distributions)中被廣泛地使用,ALSA也將被廣泛地采用。它的高級特征將幫助Linux音頻程序更好地向前發展。
Jaroslav Kysela和Takashi lwai幫助查閱了本文的草稿并提出了寶貴的意見,在此表示感謝。

總結

以上是生活随笔為你收集整理的alsa声音编程介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 色猫咪av在线 | 亚洲国产精品福利 | 亚洲大片免费 | 中文字幕.com | 激情戏网站| 美女扒开内看个够网站 | 亚洲搞av | 午夜在线成人 | 一区二区天堂 | 成人免费网址 | 国产96视频 | xvideos永久免费入口 | 欧美精品h | 欧美一区二区黄色 | 97精品超碰一区二区三区 | 日本一区二区三区视频在线播放 | 青草视频免费观看 | 成人综合站 | 少妇色综合 | 无码人妻av免费一区二区三区 | 秋霞99 | 婷婷五月综合激情 | 精品视频免费观看 | 午夜视频福利在线观看 | 97视频国产 | 国产无遮挡一区二区三区毛片日本 | 一卡二卡三卡在线观看 | 久久人人爽人人爽人人片av免费 | 久久久久伊人 | 人妻互换一二三区激情视频 | 日日热| 国产噜噜噜噜噜久久久久久久久 | 久久久久亚洲av无码专区桃色 | 96在线观看| youjizz.com在线观看 | 欧美一区二区三区四区视频 | 欧美日韩国产免费观看 | 99视频在线 | 女人扒开腿让男人桶爽 | 男人午夜视频 | 亚洲区成人 | 操伊人 | 色欲狠狠躁天天躁无码中文字幕 | av日韩不卡 | 国语对白 | 午夜视频在线看 | 亚洲色图 在线视频 | 亚洲精品久久久久av无码 | 欧美h在线观看 | 日韩在线观看视频一区二区三区 | 香蕉污视频 | se欧美| 美女赤身免费网站 | 久久亚洲综合色 | 水果视频污 | 免费av免费看 | 久久精品国产视频 | 久久久久中文字幕亚洲精品 | 国内特级毛片 | 欧美与黑人午夜性猛交久久久 | 中文字幕一区二区不卡 | 国产高清免费观看 | 国产农村妇女精品久久久 | 日韩一区二区三区四区五区 | 96久久精品 | 99国产精品视频免费观看一公开 | 日本精品视频一区二区 | 国产又粗又猛又爽 | 美女考逼 | 久草视频免费 | 性高湖久久久久久久久免费 | 日本不卡不卡 | 樱花动漫无圣光 | 亚洲色欲色欲www | 黄色av资源 | 精品无码人妻一区二区三区 | 中文字幕在线官网 | 91综合在线 | 亚洲av日韩av不卡在线观看 | 天天拍天天干 | 91们嫩草伦理| 日韩黄网 | 人妻无码一区二区三区免费 | 国产wwww | 国产在线视频网 | 成人免费毛片入口 | 国产传媒一级片 | jizz中国女人高潮 | 手机看片久久久 | 大桥未久av在线播放 | 午夜视频在线免费看 | 欧美区日韩区 | 四虎成人精品永久免费av | 中文文字幕文字幕高清 | 亚洲爆乳无码一区二区三区 | 国产又粗又硬又长又爽的演员 | 久久嫩| 91欧美日韩国产 | 欧美精品一区二区三区蜜臀 |