作者簡介
廖威雄 暨大物聯專業首批畢業生,手忙腳亂的新手奶爸,憧憬物聯未來,為天貓精靈砌磚的逐夢人
1. 背景
網上大多數是 alsa 底層框架、音頻驅動的文章,應用開發的入門少得可憐。從業務需求出發,摸索積累了一些 alsa 應用開發心得。出此文以便后來者快速入門。
本文不會涉及底層框架,也不會使用很高級的特性,適合需要做 alsa 應用開發的初學者。畢竟是半路出家,與沉浸多年對 alsa 框架了如指掌的大牛沒得比。如果有理解不準確的地方,希望指導共同進步。
-
Alsa 主頁:https://www.alsa-project.org/wiki/Main_Page
-
Alsa 文檔主頁:https://www.alsa-project.org/alsa-doc/alsa-lib/index.html
-
Alsa PCM 接口說明:https://www.alsa-project.org/alsa-doc/alsa-lib/group_pcm.html
-
Alsa HCTL 接口說明:https://www.alsa-project.org/alsa-doc/alsa-lib/group_h_control.html
-
Alsa CTL 接口說明:https://www.alsa-project.org/alsa-doc/alsa-lib/group_control.html
在學習過程中,有兩篇前人的分享給了我很多幫助,一并貼上:
-
《學習Linux操作系統中Alsa音頻編程》:http://www.xsypw.cn/emb/20190402899220.html
-
《【轉】Alsa音頻編程【精華】》:https://www.cnblogs.com/cslunatic/p/3677729.html
2. 基本概念
一些基本的概念還是要有理解的,不然無法理解 API 意義和參數作用。
我們知道聲音是靠震動傳播的,自然界傳播的聲音都是連續的模擬信號,經過采樣轉換成數字信號。其實有不少概念都是在描述怎么采樣。
2.1 樣本長度 Format
上圖中,每一個黑色小球就是采樣出來的數值,這個數值是多少比特位的,就是我們說的采樣精度,也就是樣本長度,常見的有 8 bit 和 16 bit,偶爾也有 32 bit。例如采樣值是 3,如果是 8 bit 位采樣結果就是 0x03,16 bit位采樣結果就是 0x0003。
2.2 通道數 Channels
我們常說的左聲道、右聲道,可以理解為左右各來一個 mic 采樣,左 mic 采樣出來的樣本就是左聲道數據,右邊 mic 采樣出來的樣本就是右聲道數據。而一個音頻既可以只有1個聲道,也可以有左右兩個聲道,后者也稱為立體聲。而這個音頻究竟有幾個聲道,就是我們說的通道數。
2.3 幀 Frame
我們每一次采樣出來的結果,就是一幀。很明顯,一幀數據有多大,取決于我們采樣的精度以及通道數。
2.4 交錯模式 Interleaved
我們每一次采樣出的音頻幀,怎么保存呢?提供了兩種保存思路,也就是我們說的交錯模式和非交錯模式。我們常用的也是交錯模式。
2.5 周期 Period
我們總不可能一次處理1幀數據吧,太低效了,那就做成批量處理吧。而一次處理多少幀就是我們說的周期。
一次周期結束切到下一次周期,都是需要額外處理損耗的,就類似于進程切換。周期大,一次處理數據量就多,每次連續處理時間長,切換損耗就少,但也因為數據要滿一個周期后才處理,導致數據處理延時長。反之,如果周期設置的小,延時短了,但周期切換更頻繁,損耗就更大,更容易出現卡頓。
2.6 緩存大小 Buffer Size
這里說的是 alsa 底層 DMA 搬運數據的緩存大小,這是一個環形的緩存空間。我們設置 DMA 一次連續搬運 1 個周期的數據,搬運期間如果又來數據怎么辦?我們就需要更大的緩存空間來保存更多的數據。緩存空間往往是周期的整數倍,例如設置了緩存 8 個周期,每個周期 6000 幀,那么最多可以緩存 8 * 6000 = 48000 幀的數據。
2.7 采樣率 Rate
不同于周期是人為定義的一次處理多少幀,采樣率就是固定的 1s 時間內會有多少次采樣,同時也表示 1s 播放需要多少幀。常用的采樣率如 8KHz 的人聲, 44.1KHz 的 mp3 音樂, 96Khz 的藍光音頻。
假設一個周期是 6000 幀,采樣率是 48 Khz,那么一個周期的數據能播放 125 ms。
2.8 Xrun
錄音的應用中,底層是持續不斷采樣的,如果應用程序讀取數據不夠快,底層數據緩存區還沒被取走就被新的數據覆蓋,導致數據丟失,稱為 over run。
播放的應用中,底層是持續不斷從緩存中獲取數據播放的,如果應用程序寫入數據慢了,緩存區已經沒有有效數據了,導致播放“餓死”,稱為 under run。
Xrun 是 under run 和 over run 的統稱,前者可以理解為播放卡頓,后者則是錄音卡頓。
當出現卡頓的時,大多情況調整周期、緩存大小,調整應用進程調度優先級能解決問題。
3. 框架初探與聲卡設備
以下是網上優秀的文章:
《ALSA架構簡介》:http://t.zoukankan.com/-glb-p-13722212.html
Alsa 的架構包括用戶空間的 Alsa Library,也包括內核空間的 Alsa Core 和 ASoC Core,如下圖所示:
-
APP:應用程序通過調用 alsa 庫 API 來實現聲卡播放、錄音、控制。此外,官方還提供了一些標準命令行程序,例如aplay/amixer。
-
Alsa-Library:alsa 庫封裝了底層復雜的系統調用,向上提供更直觀的 API。常見的 alsa 庫有 alsa-lib 和 tinyalsa。
-
Alsa Core:Alsa 的核心層在內核,向上提供邏輯設備、系統調用,向下驅動硬件設備。
-
ASoC Core:asoc是建立在標準 alsa core 上為更好支持嵌入式系統和移動設備音頻 codec 設計的軟件體系
Alsa 用戶空間的 API 庫主要通過 open/read/write/ioctl
操作 /dev/snd/xxx
下的設備文件實現與內核交互,常見的設備文件有:
文件名 | 用途 |
---|---|
controlC0 | 第0號聲卡的控制設備,例如音量、混音等 |
pcmC0D0c | 第0號聲卡第0個設備,用于錄音(Capture)的設備 |
pcmC0D0p | 第0號聲卡第0個設備,用于播放(Playback)的設備 |
seq | 音序器 |
timer | 定時器 |
命名規則顯而易見, pcm
表示設備類型, C0
表示聲卡0, D0
表示設備0, c/p
分別表示錄音、播放功能。
4. 系統配置與插件
最完整的介紹還是來自于官網原文:
《Asoundrc》配置文件:https://www.alsa-project.org/main/index.php/Asoundrc
《PCM (digital audio) plugins》:https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html
alsa-lib 會從 /usr/share/alsa/alsa.conf
開始加載,進而根據 alsa.conf
的記錄加載 /etc/asond.conf
和 ~/.asoundrc
。前者是系統級別的配置,后者是用戶級別的配置,兩者的語法是一致的。
我們從配置默認聲卡開始,以下是一個標準示例:
pcm.!default {
type hw
card 0
}
ctl.!default {
type hw
card 0
}
一般情況下,我們配置的格式 pcm.
,而關鍵字 !default
可以理解為保留字。pcm.!default
配置的是默認錄播聲卡, ctl.!default
配置的是默認控制聲卡。如此配置后,我們可以用 "default" 做聲卡名指代 "hw:0,0",所以下兩個命令就是等效的了。
aplay -D hw:0,0 test.wav
aplay -D default test.wav
此時 "default" 聲卡不管是播放還是錄音,都指向 "hw:0,0" 設備。如果我希望 "default" 錄音和播放指向不同(虛擬)聲卡,我們可以用 asym 插件,例如:
pcm.!default {
type asym
playback.pcm "Playback"
capture.pcm "Capture"
}
pcm.Playback {
...
}
pcm.Capture {
...
}
當然,我們可以跳過 "default" 直接用 "Playback" 的虛擬聲卡播放,例如
aplay -D Playback test.wav
Alsa 配置的節點是一個個聲卡節點串聯起來的,例如下面的配置,實現了從插件 A 開始串上插件 B ,用插件實現各種音效功能,最后到物理聲卡播放。
pcm.A {
type XXX
# 下?個節點是聲卡 B
slave.pcm B
...
}
pcm.B {
type XXX
# 下?個節點是物理聲卡
salve.pcm "hw:0,0"
}
type 字段就標識此節點使?什么插件。我們可以??實現插件,也可以?官?提供的插件,詳?官? 《PCM (digital audio)plugins》。其中有?個?常有意思的插件,這?簡單介紹下。
例如 "default" 聲卡?持分流,播放時?持?量調節,且?持混?。
pcm.!default {
type asym
playback.pcm "Playback"
capture.pcm "..."
}
pcm.Playback {
type softvol
slave.pcm PlaybackDmix
...
}
pcm.PlaybackDmix {
type plug # dmix 再套?層plug,實現重采樣
slave.pcm {
type dmix
...
slave {
pcm "hw:0,0"
format S16_LE
...
}
}
}
5.基本錄播
官?上有個最最最精簡的示例 pcm_mini,其作?僅僅是播放?段隨機數據。
精煉核?邏輯如下:
int main(void)
{
...
/* 打開alsa設備,類型為 PlayBack */
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
...
}
/* 設置alsa設備基本屬性 */
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_U8,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) {
...
}
/* 播放?頻數據 */
frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
/* 關閉聲卡設備 */
snd_pcm_close(handle);
return 0;
}
錄?跟播放?常相似,播放調? snd_pcm_writei() ,錄?則調? snd_pcm_readi() 。
總的來說,?個簡單的錄播包括以下 4 個步驟:
-
1. 打開聲卡設備
-
2. 初始化設備
-
3. 錄?、播放
-
4. 關閉聲卡設備
圍繞這 4 個步驟,介紹下常?的 API,更多的介紹請看官?:pcm api
5.1 打開聲卡設備
int snd_pcm_open(
snd_pcm_t **pcmp,
const char *name,
snd_pcm_stream_t stream,
int mode
)
-
pcmp:聲卡設備句柄,類似于?件句柄
-
name:聲卡設備名,類似于?件名,聲卡設備名參考第3章節
-
stream:數據流向,指定?于錄?還是播放
-
mode:打開模式,例如nonBlock,async等,?多數情況? 0 即可
數據流向有兩種,分別指代錄? or 播放。
?個簡單的示例如下,從 default 設備錄?:
#include
snd_pcm_t *snd_handle;
err = snd_pcm_open(&snd_handle, "default", SND_PCM_STREAM_CAPTURE, 0)
5.2初始化設備
alsa 設置聲卡參數的接??常多,可以分為軟件參數(software parameters)和硬件參數(software parameters)兩類。上?例?調?的 snd_pcm_set_params() 如官?API?檔所說,只是簡單設置軟件、硬件參數的?法,實際項?中很少這么?。
5.2.1 設置硬件參數
官?有?常多的例?,以 pcm 為例,設置硬件參數常?以下步驟:
int set_hwparams(...)
{
/* 從棧?分配硬件參數對象內存 */
snd_pcm_hw_params_alloca(¶ms);
/* 初始化參數對象 */
err = snd_pcm_hw_params_any(handle, params);
/* 設置采樣率 */
err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
/* 設置交錯模式 */
err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
/* 設置采樣格式,例如樣本?度、有?符號 */
err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 設置通道數 */
err = snd_pcm_hw_params_set_channels(handle, params, 2);
/* 設置緩存?? */
err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
/* 設置周期?? */
err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
/* 把上述設置值寫?設備 */
err = snd_pcm_hw_params(handle, params);
}
不妨回顧章節2的基本概念,可以發現硬件參數基本都是圍繞這些概念設置的。不管是交錯模式、采樣率,還是樣本?度、通道數都?較直觀,如果設置錯?概率?法正常運?。?緩存??和周期??往往跟卡頓、延時有關,且有好?種設置維度,值得展開介紹。
調整卡頓、延時參數時,我們需要記住?個核?的關系:
緩存?? = 周期?? * 周期數,即 buffer size = period size * periods
3 個參數知其2 就可以換算出另外?個參數。例如我們可以設置 周期?? 和 周期數,alsa 會?動換算出 緩存??。同理,我們可以設置緩存??和周期??,alsa 也能?動換算出周期數。在上述的例?中,就是設置了緩存??和周期??。
在?定的采樣率下,緩存??也可以換算成時間,畢竟有些?需要從時間維度設置緩存。
播放時間 = 緩存?? / 采樣率
因此我們可以發現,除了 set_buffer/period_size() 之外,我們還可以set_buffer/period_time() ,他們是等效的。
更多的 pcm 硬件參數設置API,可以看官? hw參數API?檔,這?再補充?點。
同樣是設置周期??,我們可以? snd_pcm_hw_params_set_period_size() ,但更多會選擇?
snd_pcm_hw_params_set_period_size_near() 。這?的 near 后綴表示就近設置,因為不管是緩存??還是周期??、周期數,有時候會受其他配置制約,這時候就采?可?的接近的值。例如聲卡設備在系統配置中限制了周期??不超過 1024 幀,此時如果設置 6000 幀,就會就近復位為1024。
5.2.2 設置軟件參數
同樣在 pcm 的例?中提取軟件參數設置步驟:
int set_swparams(...)
{
/* 從棧?分配軟件參數對象內存 */
snd_pcm_sw_params_alloca(¶ms);
/* 獲取當前的軟件參數配置以初始化對象 */
err = snd_pcm_sw_params_current(handle, swparams);
/* 設置起播閾值 */
err = snd_pcm_sw_params_set_start_threshold(...);
/* 設置最?可? */
err = snd_pcm_sw_params_set_avail_min(...);
/* 把上述設置值寫?設備 */
err = snd_pcm_sw_params(handle, swparams);
}
軟件參數設置我?的也不多,更多時候?脆不設置軟件參數采?默認值。為了不誤??弟,每個參數的具體作?不展開介紹。以下是相關的?檔鏈接,請讀者辨證分析。
https://blog.csdn.net/weixin_39560924/article/details/110569666
https://blog.csdn.net/zz2862625432/article/details/101787316
https://www.cnblogs.com/cslunatic/p/3677729.html
5.3錄音與播放
本?只講常?的讀寫,不展開 mmap 等?法。
交錯模式和?交錯模式?的讀(錄?)和寫(播放)接?不?樣。
# 交錯模式
snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
# ?交錯模式
snd_pcm_sframes_t snd_pcm_readn (snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
snd_pcm_sframes_t snd_pcm_writen (snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
-
pcm:聲卡設備句柄
-
buffer:讀/寫的buffer指針
-
size:指代buffer??,單位幀
?個簡單的轉播(讀->寫)示例如下:
frames = 6000; # 假設?個周期 6000 幀
buffer = malloc( frames * 4 ); # 雙通道16Bit采樣精度,因此?幀 4B
while (true) {
ret = snd_pcm_readi(chandle, buffer, frames);
snd_pcm_writei(phandle, buffer, ret);
}
5.4關閉聲卡設備
int snd_pcm_close(snd_pcm_t *pcm)
關閉聲卡設備的接??常簡單,類似于關閉?件句柄。
6. 設置音量
錄播? pcm APIs,?設置?量需要使? ctrl APIs。Alsa 有兩類控制接?,?個是 ctrl,?個則是更?層抽象的 hctrl,以下是官?兩個類型接??檔:
Alsa HCTL 接?說明:https://www.alsa-project.org/alsa-doc/alsa-lib/grouphcontrol.html
Alsa CTL 接?說明:https://www.alsa-project.org/alsa-doc/alsa-lib/group_control.html
以及下?鏈接是官?關于控制的?些概念介紹:
Control interface 概述:https://www.alsa-project.org/alsa-doc/alsa-lib/control.html
下?會先介紹關于 ctrl 的基本概念,再參考 Alsa Utils 的 amixer 命令學習如何獲取、設置?量。
6.1控制基本概念
不管是實體聲卡還是虛擬聲卡,每?個聲卡都會提供?個控制?法,我們需要設置聲卡屬性,就必須要先打開丟應的聲卡控制。打開聲卡設置需要使? snd_ctl_open() ,并?以下?種?法可以指定聲卡:
1. 使?聲卡編號,例如: hw:1
2. 使?聲卡名,例如: hw:sndtasXXXX 或者 hw:CARD=sndtasXXXX
3. 使?設備?件,例如:hw:/dev/snd/controlC0
每個聲卡可以有很多控制項,在 Alsa ?叫做 要素(Elements)。要素可以有多個成員(Member),例如?體聲有左右聲道?量兩個成員。?個要素的所有成員共享?樣的屬性,例如最?、最??量。此外,要素數據也區分類型,例如?量是整型。以下是所有?持的要素類型:
既然?個聲卡可以有很多要素(控制項),我們設置要素需要先定位哪個要素吧。要定位每個要素,可以有以下?法:
1. 使?numid:當聲卡被檢測到的時候就會賦予?個編號,但每次開機可能都不?樣。使?此編號主要是減少根據屬性遍歷時間。
2. 使?固定屬性:固定屬性包括?法類型(interface type)、設備(device)、?設備(subname)、名字(name)或者編號(index)。可以?次指定多個屬性以便準確定位要素。
6.2獲取設置音量
Alsa Libs 關于設置?量的示例不多,這時候我們不妨看看 Alsa Utils ? amixer 命令的實現,畢竟其我們通過命令?設置?量往往是通過 amixer 命令,例如:
amixer -D default cset name='Master Volume' 60 # 設置默認聲卡?量為60(要素名為:Master Volume)
6.2.1 關鍵數據類型
在了解相關代碼實現前,需要先了解?個很重要的數據類型。
1. snd_ctl_elem_id_t :記錄了定位要素的屬性,例如設備、numid等
2. snd_ctl_elem_value_t :存儲了要素值,需要根據不同類型?不同接?獲取具體值
3. snd_ctl_elem_info_t :要素的信息
對 hctl API,還有 snd_hctl_elem_t 描述具體的要素對象。
-
id 唯?標識了要素,在定位要素時可以賦值部分已知屬性到 id,?于遍歷要素。
-
通過綁定 id 后獲取要素的 info,info包含了要素的所有屬性,例如類型、完整要素 id 信息。
-
通過綁定 id 后讀取要素的 value,最后根據類型調?對應接?從 value 中獲取具體值。
6.2.2 代碼實現
以下是精簡后設置?量的實現(為了?便理解跟源代碼 amixer 的調?API不完全相同):
int cset(...)
{
/* 從棧申請 info/id/value 對象空間 */
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_value_alloca(&control);
/* 根據命令?指定的要素信息來初始化 id (id 記錄了定位要素的信息) */
snd_ctl_ascii_elem_id_parse(id, "name='Master Volume'");
/* 打開默認聲卡,獲取控制句柄 */
snd_ctl_open(&handle, "default", 0);
/* 綁定要素 id 到 info 對象,此處僅僅是把 id 賦值到 info 的成員 */
snd_ctl_elem_info_set_id(info, id);
/* 從聲卡中,依據綁定的 id,獲取要素完整的 info 信息 */
snd_ctl_elem_info(handle, info);
/* 讀取當前?量 */
/* 綁定要素 id 到 value 對象,此處僅僅是把 id 復制到 value 的成員 */
snd_ctl_elem_value_set_id(control, id);
/* 從聲卡中,依據綁定的 id,獲取要素完整的 value 信息 */
snd_ctl_elem_read(handle, control);
/* 從 value 對象中,獲取通道 idx 的整型?量值 */
vol = snd_ctl_elem_value_get_integer(control, idx);
/* 設置新?量 */
/*
* info ?記錄了要素類型、成員數量等屬性,此接?根據要素屬性,
* 解析命令?設置字符串的值,獲取新的的 value 信息。
* 此?法?于命令?字符串解析,如果是??編程實現,應該? snd_ctl_elem_value_set_xxxx()。
*/
snd_ctl_ascii_value_parse(handle, control, info, "60");
/* 把最終的 value 設置?聲卡 */
snd_ctl_elem_write(handle, control);
/* 關閉聲卡控制 */
snd_ctl_close(handle);
}
以獲取?量為例,就以下?個關鍵的步驟:
1. 初始化 id ,賦值已知的要素屬性,?便遍歷定位要素。
-
snd_ctl_ascii_elem_id_parse()
2. 綁定 id,根據 id 獲取要素 value。
-
snd_ctl_elem_value_set_id()
-
snd_ctl_elem_read()
3. (默認?量是int類型)調? int 類型獲取接?,從 value 對象獲取實際?量值
-
snd_ctl_elem_value_get_integer()
設置?量與獲取?量相?,多了以下?個步驟:
1. (默認?量是int類型)調? int 類型設置接?,設置 value 對象新?量值
-
snd_ctl_elem_value_set_integer()
2. 把 value 對象寫?聲卡
-
snd_ctl_elem_write()
當然,如果想要做的兼容性更好,我們還需要獲取要素 info,以根據 info 記錄的要素類型調?不同接?:
1. 綁定 id,根據 id 獲取要素 info
-
snd_ctl_elem_info_set_id()
-
snd_ctl_elem_info()
2. 從 info 獲取要素類型、要素成員數量、完整的id信息等
-
snd_ctl_elem_info_get_type()
-
snd_ctl_elem_info_get_count()
-
snd_ctl_elem_info_get_id()
補充?點,我們可以直接? snd_ctl_elem_id_set_numid/name/index/... 直接初始化 id,也可以參考 amixer 通過字符串??解析初始化 id,調? snd_ctl_ascii_elem_id_parse() 。??解析字符串?持以下格式:
[[iface=,][name='name',][index=,][device=,][subdevice=]]|[numid=]
7. 調試信息
調試信息用于打印聲卡詳細的屬性,類似于 aplay 命令的 -v
選項。
alsa debug API文檔:https://www.alsa-project.org/alsa-doc/alsa-lib/grouppcmdump.html
以下是我常用的一個例子:
/* 定義dump輸出對象 */
snd_output_t *output = NULL;
/* 綁定輸出對象到 stdout */
snd_output_stdio_attach(&output, stdout, 0);
/* dump 出聲卡 handle (pcm)的信息 */
snd_pcm_dump(handle, output);
/* 關閉輸出對象 */
snd_output_close(output);
snd_pcm_dump() 可以dump出播放鏈路中每?個節點的配置信息,例如 dmix 插件的信息:
Slave: Direct Stream Mixing PCM
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : xxxx
exact rate : xxxx (xxxx/1)
msbits : xxx
buffer_size : xxx
period_size : xxx
period_time : xxx
tstamp_mode : NONE
tstamp_type : GETTIMEOFDAY
period_step : 1
avail_min : 6000
period_event : 0
start_threshold : xxx
stop_threshold : xxxx
silence_threshold: 0
silence_size : 0
boundary : 5066549580791808000
當然,如果想看聲卡的 hw/sw_params,也可以直接讀 proc 的?件,例如聲卡0的播放設備0節點:
cat /proc/asound/card0/pcm0p/sub0/{hw_params,sw_params}
8. 命令工具集
Alsa Utils 提供了?系列?常有?的?具集,常?的包括 arecord 錄?、aplay 播放、amixer 設置。
每個命令都有詳細的 --help 信息,本?只提供?個簡單的例?。
# 從 default 設備錄?,采樣精度為 16 bit,采樣率為16K,1通道
arecord -D default -f S16_LE -r 16000 -c 1 ./record.wav
# 向 default 設備播放
# wav 可以??從頭信息讀取,PCM格式需要指定更多參數,不?持mp3等需要解碼的?頻格式
aplay -D default ./record.wav
# 修改系統?量為90
amixer -D default cset name='xxxxx Volume' 90
原文標題:8. 命令工具集
文章出處:【微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
-
接口
+關注
關注
33文章
8601瀏覽量
151168 -
框架
+關注
關注
0文章
403瀏覽量
17489 -
alsa
+關注
關注
0文章
19瀏覽量
3618
原文標題:8. 命令工具集
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論