Linux 音频开发之入门篇
前言
? ? ? 其實為什么要采用兩種框架呢?這里就不做記錄了。只關心不同的應用場景。
? ? 從編程易用性上來講,OSS倒是簡便,然而發現ubuntu上默認不配置OSS,導致連相關設備節點都沒有。因而不得不采用ALSA,此編程接口比較啰嗦。
? ? ? 根據 arch OSS的wiki??Open Sound System - ArchWiki (archlinux.org)? ? ? 兩者的比較優缺點主要:(順便說句arch的wiki資料比較齊全?)
OSS Advantages (users)
- Per-application volume control.
- Some legacy (i.e. before 2002) cards can have better support.
OSS Advantages (developers)
- Support for drivers in userspace.
- Cross-platform (OSS runs on BSDs and Solaris).
- Smaller and easier to use API. (這點筆者在實際使用中感受很深,主要了解清楚哪些ioctl干啥的就可以,符合驅動的編寫思路)
ALSA advantages over OSS
- Better support for USB audio devices.
- Support for Bluetooth audio devices.
- Support for?AC'97?and?HD Audio?dial-up soft-modems such as Si3055.
- Better support for MIDI devices.
- Support for suspend.
- Better support for jack detection.
- Better support for modern hardware.
Note:
- OSS has experimental output support for USB audio devices, but no input.
- OSS supports MIDI devices with the help of a software synthesizer such as?Timidity?or?FluidSynth.
聲道
? ? ?什么是聲道? 其是個硬件概念還是軟件概念?
? ? ?最終聲道需要由物理接口輸出。而數據的產生則由軟件控制。
? ? ? 聲音的傳播分為不同頻率。一個聲卡的聲音輸出可以支持不同的頻率。例如前聲道輸入44K,后聲道輸出8K。數字數據交錯存儲在內存中,聲卡在取出數據后,將數據分別送往不同的聲道,產生不同的聲音。
? ? 寫到這里,其實交錯存儲的數據,是否也存在這種情況?即由于采用頻率不同,導致每個聲道的數據長度不一致,進而占用的空間數量不同。例如下面這種情況: 三個L數據后,才是一個R數據?
? ?
硬件接口
音頻的基本框架
編程接口
linux 下音頻編程的接口分為兩種OSS和ALSA,這是老生常談的。
OSS接口
官網說明??OSS v4.x API reference - Developing applications for Open Sound System version 4.1
通過 open? write read mmap等系統調用進行訪問。
?設備節點
?為了通過open 等相關接口對設備進行控制,首先要知道其設備節點。根據
OSS v4.x API reference - Device types supported by OSS (opensound.com)
?我們可以知道,OSS支持三種設備類型:
Open Sound System supports three kind of devices:
- Audio devices are used to play and record digital audio such as WAV or MP3 files. See the "Audio Programming" chapter for more info. 控制音頻的輸入和輸出。也就是數據通道!此類設備對應/dev/dsp? /dev/dsp1等。此處每次重新上電后,/dev/dsp? /dev/dsp1實際對應的物理設備會發生變化!在打開設備后,我們要先查詢此設備的具體信息 ,再進行操作。
- Mixer devices are used to change volumes and all kind of "control panel" settings. See the "OSS Mixer Programming" chapter for more info. 用于調節音量,對應的設備節點為/dev/mixer等。
- MIDI port devices are used to access musical instruments (keyboards, samplers and synthesizer modules) as well as to control various other devices such as mixers, effect processors, on stage lightning and pyrotecnics. See the "OSS MIDI Programming" chapter for more info
IOCTL接口
參考連接:?OSS v4.x API reference - The ioctl() system call (opensound.com)
?連接中介紹各個ioctl,例如筆者想要確認當前卡的信息,可以采用
示例:?OSS v4.x API reference - The ossinfo program that is included in the OSS package. (opensound.com)
在實際使用過程中,查閱是否有合適的接口。
ALSA接口
設備節點
ALSA Library API - AlsaProject (alsa-project.org)? 根據ALSA wiki介紹,設備節點為:
he currently designed interfaces are listed below:
這里存在的問題,我們并不是是對這些設備節點進行訪問來進行錄音及播放,而是采用例如 default? 或者?plughw:0,0 等作為參數,而這些具體對應哪個物理設備,是不是我們期望的設備?這就又增加了一層不友好的屏蔽。
例如在參考資料3)中的例程中,默認打開default設備,在x86虛擬機上運行正常;但是交叉編譯到arm嵌入式環境就不能運行,打開設備錯誤。而改成plughw:0,0就能正常打開了。
----》》》可以通過lsof命令查看當前哪些進程會操作上述設備節點:
lsof |grep snd/cpulseaudi 1150 gdm 32u CHR 116,6 0t0 448 /dev/snd/controlC0 pulseaudi 1150 gdm 39u CHR 116,6 0t0 448 /dev/snd/controlC0 snapd-gli 1150 1202 gdm 32u CHR 116,6 0t0 448 /dev/snd/controlC0 snapd-gli 1150 1202 gdm 39u CHR 116,6 0t0 448 /dev/snd/controlC0 alsa-sink 1150 1338 gdm 32u CHR 116,6 0t0 448 /dev/snd/controlC0 alsa-sink 1150 1338 gdm 39u CHR 116,6 0t0 448 /dev/snd/controlC0 alsa-sour 1150 1356 gdm 32u CHR 116,6 0t0 448 /dev/snd/controlC0 alsa-sour 1150 1356 gdm 39u CHR 116,6 0t0 448 /dev/snd/controlC0信息接口設備節點示例:
1)獲取單板上的卡的數目及名稱
cat /proc/asound/cards0 [audiocodec ]: audiocodec - audiocodecaudiocodec1 [sndhdmi ]: sndhdmi - sndhdmisndhdmi?2)獲取某塊聲卡的軟硬件參數??梢钥吹侥壳坝布禌]有配置,沒有在使用此聲卡。
cat /proc/asound/card0/pcm0p/info 播放參數 card: 0 device: 0 subdevice: 0 stream: PLAYBACK id: SUNXI-CODEC sun8iw11codec-0 name: subname: subdevice #0 class: 0 subclass: 0 subdevices_count: 1 subdevices_avail: 1 root@T3/A40i-Tronlong:~# cat /proc/asound/card0/pcm0p/sub0/ hw_params info status sw_params root@T3/A40i-Tronlong:~# cat /proc/asound/card0/pcm0p/sub0/hw_params 硬件參數 closed當運行speaker-test 命令再查看硬件參數信息如下:
cat /proc/asound/card0/pcm0p/sub0/statusstate: RUNNING owner_pid : 3829 (當前進程信息都有) trigger_time: 5889.590491875 tstamp : 0.000000000 delay : 32768 avail : 0 avail_max : 24576 ----- hw_ptr : 15196160 appl_ptr : 15228928$ cat /proc/asound/card0/pcm0p/sub0/hw_paramsaccess: RW_INTERLEAVED format: S16_LE subformat: STD channels: 1 rate: 48000 (1572864000/32768) period_size: 8192 buffer_size: 32768設備名稱
ALSA 分了幾層,每層都有一個設備名稱,通過配置文件指定其下一層的具體設備信息
通過配置文件建立關聯。(配置文件示例及分層結構待補充)
如果沒有配置文件,則直接指定物理設備,類似于下面 -D后的參數:
aplay -t raw -r 8000 -q -D hw:/dev/snd/controlC0 -f u8 -c 1open接口
? 參數含義: 這里最難以填寫的參數是 name。針對播放錄音,很多例程都填寫default,在我們實際測試中,也可以填寫?plughw:0,0? 和 hw:/dev/snd/controlC0。
? ? 但是不能填寫?hw:/dev/snd/pcmC0D0p,填寫此設備節點時,運行時報錯:?
ALSA lib pcm_hw.c:1713:(_snd_pcm_hw_open) Invalid value for card
unable to open pcm device: Inappropriate ioctl for device
這里理論上這個節點是pcm 的播放節點,理論上設置這個才OK的嘛!!一團糟糕的設計!!!
snd_pcm_open() int snd_pcm_open ( snd_pcm_t ** pcmp, const char * name, snd_pcm_stream_t stream, int mode ) Opens a PCM.Parameters pcmp Returned PCM handle name ASCII identifier of the PCM handle stream Wanted stream mode Open mode (see SND_PCM_NONBLOCK, SND_PCM_ASYNC) Returns 0 on success otherwise a negative error code Examples /test/latency.c, /test/pcm.c, and /test/pcm_min.c.常見錯誤
? ? ? 設備可以打開play ,但是打開record時失敗,由如下代碼知,錯誤信息為resource busy,即設備被占用。
if ((err = snd_pcm_open (&handle,"hw:/dev/snd/controlC0" , type, 0)) < 0) {fprintf (stderr, "cannot open (%s)\n",snd_strerror (err));return NULL;}? ? ? 這時,由于我們還不熟悉,以為第二個參數傳遞的有問題,實際上是由于代碼中存在while死循環一直接收數據或者發生數據,在用戶按ctrl+C時,沒有調用接口釋放 設備節點導致。
獲取硬件參數能力值
? ?不能配置不在能力范圍內的值。
? 具體接口參數? 待補充!!!!!!!!!!!!!!!
配置硬件參數
硬件參數包括(參考資料3 中描述)
采樣率 Rate
?An analog-to-digital converter (ADC) converts the analog voltages into discrete values, called samples, at regular intervals in time, known as the sampling rate
?樣本長度 Format
The size of the samples, expressed in bits, is one factor that determines how accurately the sound is represented in digital form
采用用多少位數據表示,部分宏定義如下圖,詳細的參見:?ALSA project - the C library reference: PCM Interface (alsa-project.org)
參數需要硬件支持,例如在T3板子上配置U8,則提示配置不了。而在X86虛擬機上則配置正常。
強行配置U8,報如下錯誤:
cannot set sample format (Invalid argument)那么通過哪個接口來獲取當前硬件支持的情況呢?!!
?
問題:不同的采樣率 是否需要不同的采樣數據長度?例如44.1K的采樣是否能用 8位數據表示?
?通道數 Channels
我們常說的左聲道、右聲道,可以理解為左右各來一個 mic 采樣,左 mic 采樣出來的樣本就是左聲道數據,右邊 mic 采樣出來的樣本就是右聲道數據。而一個音頻既可以只有1個聲道,也可以有左右兩個聲道,后者也稱為立體聲。而這個音頻究竟有幾個聲道,就是我們說的通道數。
?幀 Frame
我們每一次采樣出來的結果,就是一幀。很明顯,一幀數據有多大,取決于我們采樣的精度以及通道數。
?交錯模式 Interleaved
我們每一次采樣出的音頻幀,怎么保存呢?提供了兩種保存思路,也就是我們說的交錯模式和非交錯模式。我們常用的也是交錯模式。
周期 Period
我們總不可能一次處理1幀數據吧,太低效了,那就做成批量處理吧。而一次處理多少幀就是我們說的周期。
一次周期結束切到下一次周期,都是需要額外處理損耗的,就類似于進程切換。周期大,一次處理數據量就多,每次連續處理時間長,切換損耗就少,但也因為數據要滿一個周期后才處理,導致數據處理延時長。反之,如果周期設置的小,延時短了,但周期切換更頻繁,損耗就更大,更容易出現卡頓。
緩存大小 Buffer Size
這里說的是 alsa 底層 DMA 搬運數據的緩存大小,這是一個環形的緩存空間。我們設置 DMA 一次連續搬運 1 個周期的數據,搬運期間如果又來數據怎么辦?我們就需要更大的緩存空間來保存更多的數據。緩存空間往往是周期的整數倍,例如設置了緩存 8 個周期,每個周期 6000 幀,那么最多可以緩存 8 * 6000 = 48000 幀的數據。
系統命令
OSS shell命令
ossinfo
ALSA shell命令
root執行報錯問題
錯誤信息類似:
./x86csdn MoTTY X11 proxy: Unsupported authorisation protocol xcb_connection_has_error() returned true XDG_RUNTIME_DIR (/run/user/1000) is not owned by us (uid 0), but by uid 1000! (This could e g happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.) ALSA lib pcm_dsnoop.c:618:(snd_pcm_dsnoop_open) unable to open slave cannot open for capturepulseaudio來管理音頻設備的,而pulseaudio不允許在root用戶下運行,這樣會導致安全問題。
解決方法:跨用戶調用命令 【可用】
su - username -c "aplay sample.wav"?同樣的命令,root下直接./x86csdn報如上錯誤。而采用如下形式就可以正常運行了!!?
su - xiehj -c "/home/user/sound/x86csdn"amixer
音量調控
1)amixer ?info
Card default 'audiocodec'/'audiocodec'
? Mixer name ? ?: ''
? Components ? ?: ''
? Controls ? ? ?: 48
? Simple ctrls ?: 48
子命令
Available commands:
? scontrols ? ? ? show all mixer simple controls
? scontents ? ? ? show contents of all mixer simple controls (default command)
? sset sID P ? ? ?set contents for one mixer simple control
? sget sID ? ? ? ?get contents for one mixer simple control
? controls ? ? ? ?show all controls for given card? (是否mixer simple controls只是controls中的一個類型?!)
? contents ? ? ? ?show contents of all controls for given card
? cset cID P ? ? ?set control contents for one control
? cget cID ? ? ? ?get control contents for one control
root@T3/A40i-Tronlong:~# amixer scontrols (此命令輸出與controls命令輸出類似)
Simple mixer control 'Headphone',0
Simple mixer control 'Headphone volume',0
Simple mixer control 'FM gain volume',0
Simple mixer control 'Phone Out Mixer LOMIX',0
Simple mixer control 'Phone Out Mixer MIC1 Boost',0
Simple mixer control 'Phone Out Mixer MIC2 Boost',0
Simple mixer control 'Phone Out Mixer ROMIX',0
Simple mixer control 'Phoneout Speaker',0
Simple mixer control 'ADC gain volume',0
Simple mixer control 'HPL Mux',0
Simple mixer control 'HPR Mux',0
Simple mixer control 'LINEIN Mixer volume',0
Simple mixer control 'LINEIN gain volume',0
Simple mixer control 'Left Input Mixer FML',0
Simple mixer control 'Left Input Mixer LINEINL',0
Simple mixer control 'Left Input Mixer LINEINLR',0
Simple mixer control 'Left Input Mixer LOMIX',0
Simple mixer control 'Left Input Mixer MIC1 Boost',0
Simple mixer control 'Left Input Mixer MIC2 Boost',0
Simple mixer control 'Left Input Mixer ROMIX',0
Simple mixer control 'Left Output Mixer DACL',0
Simple mixer control 'Left Output Mixer DACR',0
Simple mixer control 'Left Output Mixer FML',0
Simple mixer control 'Left Output Mixer LINEINL',0
Simple mixer control 'Left Output Mixer LINEINLR',0
Simple mixer control 'Left Output Mixer MIC1 Boost',0
Simple mixer control 'Left Output Mixer MIC2 Boost',0
Simple mixer control 'MIC gain volume',0
Simple mixer control 'MIC1 boost volume',0
Simple mixer control 'MIC2 Mux',0
Simple mixer control 'MIC2 boost volume',0
Simple mixer control 'Right Input Mixer FMR',0
Simple mixer control 'Right Input Mixer LINEINLR',0
Simple mixer control 'Right Input Mixer LINEINR',0
Simple mixer control 'Right Input Mixer LOMIX',0
Simple mixer control 'Right Input Mixer MIC1 Boost',0
Simple mixer control 'Right Input Mixer MIC2 Boost',0
Simple mixer control 'Right Input Mixer ROMIX',0
Simple mixer control 'Right Output Mixer DACL',0
Simple mixer control 'Right Output Mixer DACR',0
Simple mixer control 'Right Output Mixer FMR',0
Simple mixer control 'Right Output Mixer LINEINLR',0
Simple mixer control 'Right Output Mixer LINEINR',0
Simple mixer control 'Right Output Mixer MIC1 Boost',0
Simple mixer control 'Right Output Mixer MIC2 Boost',0
Simple mixer control 'codec hub mode',0
Simple mixer control 'digital volume',0
Simple mixer control 'phoneout volume',0
root@T3/A40i-Tronlong:~# amixer controls (如果支持其他iface,可能會和scontrols的輸出有所不同。那么這些controls具體有物理器件對應?)
numid=3,iface=MIXER,name='Headphone volume'
numid=47,iface=MIXER,name='Headphone Switch'
numid=5,iface=MIXER,name='FM gain volume'
numid=15,iface=MIXER,name='Phone Out Mixer LOMIX Switch'
numid=18,iface=MIXER,name='Phone Out Mixer MIC1 Boost Switch'
numid=17,iface=MIXER,name='Phone Out Mixer MIC2 Boost Switch'
numid=16,iface=MIXER,name='Phone Out Mixer ROMIX Switch'
numid=48,iface=MIXER,name='Phoneout Speaker Switch'
numid=11,iface=MIXER,name='ADC gain volume'
numid=13,iface=MIXER,name='HPL Mux'
numid=14,iface=MIXER,name='HPR Mux'
numid=4,iface=MIXER,name='LINEIN Mixer volume'
numid=6,iface=MIXER,name='LINEIN gain volume'
numid=28,iface=MIXER,name='Left Input Mixer FML Switch'
numid=29,iface=MIXER,name='Left Input Mixer LINEINL Switch'
numid=30,iface=MIXER,name='Left Input Mixer LINEINLR Switch'
numid=27,iface=MIXER,name='Left Input Mixer LOMIX Switch'
numid=32,iface=MIXER,name='Left Input Mixer MIC1 Boost Switch'
numid=31,iface=MIXER,name='Left Input Mixer MIC2 Boost Switch'
numid=26,iface=MIXER,name='Left Input Mixer ROMIX Switch'
numid=41,iface=MIXER,name='Left Output Mixer DACL Switch'
numid=40,iface=MIXER,name='Left Output Mixer DACR Switch'
numid=42,iface=MIXER,name='Left Output Mixer FML Switch'
numid=43,iface=MIXER,name='Left Output Mixer LINEINL Switch'
numid=44,iface=MIXER,name='Left Output Mixer LINEINLR Switch'
numid=46,iface=MIXER,name='Left Output Mixer MIC1 Boost Switch'
numid=45,iface=MIXER,name='Left Output Mixer MIC2 Boost Switch'
numid=7,iface=MIXER,name='MIC gain volume'
numid=9,iface=MIXER,name='MIC1 boost volume'
numid=12,iface=MIXER,name='MIC2 Mux'
numid=10,iface=MIXER,name='MIC2 boost volume'
numid=21,iface=MIXER,name='Right Input Mixer FMR Switch'
numid=23,iface=MIXER,name='Right Input Mixer LINEINLR Switch'
numid=22,iface=MIXER,name='Right Input Mixer LINEINR Switch'
numid=19,iface=MIXER,name='Right Input Mixer LOMIX Switch'
numid=25,iface=MIXER,name='Right Input Mixer MIC1 Boost Switch'
numid=24,iface=MIXER,name='Right Input Mixer MIC2 Boost Switch'
numid=20,iface=MIXER,name='Right Input Mixer ROMIX Switch'
numid=33,iface=MIXER,name='Right Output Mixer DACL Switch'
numid=34,iface=MIXER,name='Right Output Mixer DACR Switch'
numid=35,iface=MIXER,name='Right Output Mixer FMR Switch'
numid=37,iface=MIXER,name='Right Output Mixer LINEINLR Switch'
numid=36,iface=MIXER,name='Right Output Mixer LINEINR Switch'
numid=39,iface=MIXER,name='Right Output Mixer MIC1 Boost Switch'
numid=38,iface=MIXER,name='Right Output Mixer MIC2 Boost Switch'
numid=1,iface=MIXER,name='codec hub mode'
numid=2,iface=MIXER,name='digital volume'
numid=8,iface=MIXER,name='phoneout volume'
amixer 設備選擇示例
? ?例如針對一個聲卡設備,其設備節點為/dev/snd/controlC0,即支持front microphone 又支持rear microphone,也就是存在兩個capture設備。那么我們如何選擇其中一個呢?
? ? 讓我們再來看下mixer的功能:
-
Mixer interface: controls the devices on sound cards that route signals and control volume levels. It is built on top of the control interface.
? ? ?此處講明了 mixer 除了控制音量的功能,還有一個控制信號路由的功能,用此功能,我們可以選擇具體哪個設備對應 /dev/snd/controlC0的capture 功能。
? ? 示例:
? ? ?1) 首先查詢當前mixer的信息? ? ??
amixer contents 通過如下命令的輸出,我們可以看到 input source 對應又兩個選項: front mic和rear mic 而values = 1,表示此時采用rear mic numid=10,iface=MIXER,name='Input Source'; type=ENUMERATED,access=rw------,values=1,items=2; Item #0 'Front Mic'; Item #1 'Rear Mic': values=1numid=17,iface=MIXER,name='Rear Mic Boost Volume'; type=INTEGER,access=rw---R--,values=2,min=0,max=3,step=0: values=2,2| dBscale-min=0.00dB,step=10.00dB,mute=0numid=16,iface=MIXER,name='Front Mic Boost Volume'; type=INTEGER,access=rw---R--,values=2,min=0,max=3,step=0: values=0,0| dBscale-min=0.00dB,step=10.00dB,mute=02) 更改輸入硬件源:
amixer cset numid=10 0 最后一個參數即選項的索引numid=10,iface=MIXER,name='Input Source'; type=ENUMERATED,access=rw------,values=1,items=2; Item #0 'Front Mic'; Item #1 'Rear Mic': values=0音量調節示例
| ? 英文 | 漢語 |
| volume | 音量 |
| unmute | 取消靜音 |
amixer -c 1 sset Line,0 80%,40% unmute cap
will set the second soundcard's left line input volume to 80% and right line input to 40%, unmute it, and select it as a source for capture (recording).
這里 Line,0 表示左側輸入音量,那么右側輸入音量是Line,1 ?
amixer -c 1 -- sset Master playback -20dB
will set the master volume of the second card to -20dB. If the master has multiple channels, all channels are set to the same value.
amixer -c 1 set PCM 2dB+
will increase the PCM volume of the second card with 2dB. When both playback and capture volumes exist, this is applied to both volumes.
amixer -c 2 cset iface=MIXER,name='Line Playback Volume",index=1 40%
will set the third soundcard's second line playback volume(s) to 40%
amixer -c 2 cset numid=34 40%
will set the 34th soundcard element to 40%
音量調節實戰
環境基于ubuntu 虛擬機 x86
amixer info
Card default 'AudioPCI'/'Ensoniq AudioPCI ENS1371 at 0x2040, irq 16'
? Mixer name ? ?: 'Cirrus Logic CS4297A rev 3'
? Components ? ?: 'AC97a:43525913'
? Controls ? ? ?: 26
? Simple ctrls ?: 13
amixer scontents
Simple mixer control 'Master',0
? Capabilities: pvolume pswitch pswitch-joined
? Playback channels: Front Left - Front Right
? Limits: Playback 0 - 63
? Mono:
? Front Left: Playback 63 [100%] [0.00dB] [on]
? Front Right: Playback 63 [100%] [0.00dB] [on]
Simple mixer control 'PCM',0
? Capabilities: pvolume pswitch pswitch-joined
? Playback channels: Front Left - Front Right
? Limits: Playback 0 - 63
? Mono:
? Front Left: Playback 48 [76%] [37.50dB] [on]
? Front Right: Playback 48 [76%] [37.50dB] [on]
Simple mixer control 'Line',0
? Capabilities: pvolume pswitch pswitch-joined cswitch cswitch-exclusive
? Capture exclusive group: 0
? Playback channels: Front Left - Front Right
? Capture channels: Front Left - Front Right
? Limits: Playback 0 - 63
? Front Left: Playback 0 [0%] [-34.50dB] [off] Capture [off]
? Front Right: Playback 0 [0%] [-34.50dB] [off] Capture [off]
初始音量信息
?
更改音量操作
在圖形界面更改音量,然后通過命令查詢
amixer scontents,通過命令可以看到pcm的音量值發生變化,
Simple mixer control 'Master',0
? Capabilities: pvolume pswitch pswitch-joined
? Playback channels: Front Left - Front Right
? Limits: Playback 0 - 63
? Mono:
? Front Left: Playback 63 [100%] [0.00dB] [on]
? Front Right: Playback 63 [100%] [0.00dB] [on]
Simple mixer control 'PCM',0
? Capabilities: pvolume pswitch pswitch-joined
? Playback channels: Front Left - Front Right
? Limits: Playback 0 - 63
? Mono:
? Front Left: Playback 56 [89%] [49.50dB] [on]
? Front Right: Playback 56 [89%] [49.50dB] [on]
?
輸入音量的控制
注意下面這個圖,和上面輸入音量的截圖相比,設備也更換了,因為microphone這個設備調節了后,命令查不出改變。
Simple mixer control 'Capture',0
? Capabilities: cvolume cswitch cswitch-joined
? Capture channels: Front Left - Front Right
? Limits: Capture 0 - 15
? Front Left: Capture 4 [27%] [6.00dB] [on]
? Front Right: Capture 4 [27%] [6.00dB] [on]
?
經過上述的實戰,我們依然不能明了當前系統使用的是哪個設備!!
不過對于某開發板:
?其絲印標識 H/P OUT? 和LINE IN ,看起來可以和amixer 獲取到的上圖信息對應。
master、PCM等的關系
?調節不同節點影響的范圍是怎么樣的?
alsactl ?monitor
主要功能監控 alsa ,通過再界面操作,我們可以了解到界面具體操控了哪些設備,這樣我們就可以用amixer等命令進行配置了
node hw:0, #4 (2,0,0,Headphone Playback Switch,0) VALUE
node hw:0, #3 (2,0,0,Headphone Playback Volume,0) VALUE
node hw:0, #18 (2,0,0,Master Playback Volume,0) VALUE
node hw:0, #62 (2,0,0,PCM Playback Volume,0) VALUE
node hw:0, #4 (2,0,0,Headphone Playback Switch,0) VALUE
node hw:0, #4 (2,0,0,Headphone Playback Switch,0) VALUE
node hw:0, #3 (2,0,0,Headphone Playback Volume,0) VALUE
node hw:0, #18 (2,0,0,Master Playback Volume,0) VALUE
node hw:0, #62 (2,0,0,PCM Playback Volume,0) VALUE
編程實踐
OSS編程實踐
聲卡信息獲取
? ?為什么我們需要這個功能,因為我們要區分哪個設備節點為我們期望操作的?
音量調節
? 為什么我們需要這個功能,因為不同的音量導致輸出 、輸入數據的變化。?
輸入輸出
?為什么我們需要這個功能,因為這個是音頻的數據通道。
ALSA編程實踐
錄音與播放
? ? ? 分離的錄音與播放功能,參見 參考資料3 中的示例,比較清晰。同時,參考資料3 中的另外一篇實現了邊播邊放,本質是將 設備節點作為play 、capture同時打開,然后分別對play和capture句柄分別進行寫 、讀操作。
實驗要求及代碼
? ? ?以上的示例中,通常都將rate 設置為44.1K, channel 為2,以及采用精度設置為S16。
? ? 本文實驗將rate改成8K, channel改為1,精度改為U8。示例代碼如下,主體代碼為參考資料3中,主要修改 硬件參數進行測試。
#include <stdio.h> #include <stdlib.h> #include <alsa/asoundlib.h>snd_pcm_t *open_sound_dev(snd_pcm_stream_t type) {int err;snd_pcm_t *handle;snd_pcm_hw_params_t *hw_params;//unsigned int rate = 44100;unsigned int rate = 8000; /* int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)pcmp 打開的pcm句柄 name 要打開的pcm設備名字,默認default,或者從asound.conf或者asoundrc里面選擇所要打開的設備 stream SND_PCM_STREAM_PLAYBACK 或 SND_PCM_STREAM_CAPTURE,分別表示播放和錄音的PCM流 mode 打開pcm句柄時的一些附加參數 SND_PCM_NONBLOCK 非阻塞打開(默認阻塞打開), SND_PCM_ASYNC 異步模式打開 返回值 0 表示打開成功,負數表示失敗,對應錯誤碼 "plughw:0,0" */if ((err = snd_pcm_open (&handle,"hw:/dev/snd/controlC0" , type, 0)) < 0) {fprintf (stderr, "cannot open (%s)\n",snd_strerror (err));return NULL;} /* snd_pcm_hw_params_malloc( ) 在棧中分配 snd_pcm_hw_params_t 結構的空間,然后使用 snd_pcm_hw_params_any( ) 函數用聲卡的全配置空間參數初始化已經分配的 snd_pcm_hw_params_t 結構。snd_pcm_hw_params_set_access ( ) 設置訪問類型,常用訪問類型的宏定義有:SND_PCM_ACCESS_RW_INTERLEAVED 交錯訪問。在緩沖區的每個 PCM 幀都包含所有設置的聲道的連續的采樣數據。比如聲卡要播放采樣長度是 16-bit 的 PCM 立體聲數據,表示每個 PCM 幀中有 16-bit 的左聲道數據,然后是 16-bit 右聲道數據。SND_PCM_ACCESS_RW_NONINTERLEAVED 非交錯訪問。每個 PCM 幀只是一個聲道需要的數據,如果使用多個聲道,那么第一幀是第一個聲道的數據,第二幀是第二個聲道的數據,依此類推。函數 snd_pcm_hw_params_set_format() 設置數據格式,主要控制輸入的音頻數據的類型、無符號還是有符號、是 little-endian 還是 bit-endian。比如對于 16-bit 長度的采樣數據可以設置為: */ if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",snd_strerror (err));return NULL;}if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",snd_strerror (err));return NULL;}if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {// 交錯訪問fprintf (stderr, "cannot set access type (%s)\n",snd_strerror (err));return NULL;}if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_U8)) < 0) { // SND_PCM_FORMAT_U8 SND_PCM_FORMAT_S16_LE 有符號16 bit Little Endianfprintf (stderr, "cannot set sample format (%s)\n",snd_strerror (err));return NULL;}if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) { //snd_pcm_hw_params_set_rate_near () 函數設置音頻數據的最接近目標的采樣率fprintf (stderr, "cannot set sample rate (%s)\n",snd_strerror (err));return NULL;}if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 1)) < 0) {fprintf (stderr, "cannot set channel count (%s)\n",snd_strerror (err));return NULL;} //snd_pcm_hw_params( ) 從設備配置空間選擇一個配置,讓函數 snd_pcm_prepare() 準備好 PCM 設備,以便寫入 PCM 數據。if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {fprintf (stderr, "cannot set parameters (%s)\n",snd_strerror (err));return NULL;}snd_pcm_hw_params_free (hw_params);return handle; }void close_sound_dev(snd_pcm_t *handle) {snd_pcm_close (handle); }snd_pcm_t *open_playback(void) {return open_sound_dev(SND_PCM_STREAM_PLAYBACK); }snd_pcm_t *open_capture(void) {return open_sound_dev(SND_PCM_STREAM_CAPTURE); }int main (int argc, char *argv[]) {int err;unsigned char buf[512];unsigned char recv_buf[512];snd_pcm_t *playback_handle;snd_pcm_t *capture_handle;int i=0;playback_handle = open_playback();if (!playback_handle){fprintf (stderr, "cannot open for playback\n");return -1;}//snd_pcm_close (playback_handle);capture_handle = open_capture();if (!capture_handle){fprintf (stderr, "cannot open for capture\n");snd_pcm_close (playback_handle);return -1;}if ((err = snd_pcm_prepare (playback_handle)) < 0) {fprintf (stderr, "cannot prepare audio interface for use (%s)\n",snd_strerror (err));snd_pcm_close (playback_handle);snd_pcm_close (capture_handle);return -1;}if ((err = snd_pcm_prepare (capture_handle)) < 0) {fprintf (stderr, "cannot prepare audio interface for use (%s)\n",snd_strerror (err));return -1;} #if 1while (1) {if ((err = snd_pcm_readi (capture_handle, recv_buf, 128)) != 128) {fprintf (stderr, "read from audio interface failed (%s)\n",snd_strerror (err));snd_pcm_close (playback_handle);snd_pcm_close (capture_handle);return -1;}printf("recv data:");for(i=0;i<64;i++)printf("0x%x ",recv_buf[i]);printf("\n");memset(buf,0x55,128);memset(buf,0x0,128);if ((err = snd_pcm_writei (playback_handle, buf, 128)) != 128) { //snd_pcm_writei() 用來把交錯的音頻數據寫入到音頻設備。fprintf (stderr, "write to audio interface failed (%s)\n",snd_strerror (err));snd_pcm_close (playback_handle);snd_pcm_close (capture_handle);return -1;}} #endif snd_pcm_close (playback_handle);snd_pcm_close (capture_handle);return 0; }運行結果及問題
運行后,很快出現 broken pipe,而原先未修改硬件配置參數的代碼則未出現此問題。異常信息如下:
./x86csdn MoTTY X11 proxy: Unsupported authorisation protocol xcb_connection_has_error() returned true XDG_RUNTIME_DIR (/run/user/1000) is not owned by us (uid 0), but by uid 1000! (This could e g happen if you try to connect to a non-root PulseAudio as a root user, over the native protocol. Don't do that.) recv data:0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 recv data:0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 write to audio interface failed (Broken pipe)問題分析 之 設備恢復及統計
?如下,通過接口pcm_prepare恢復設備狀態。并通過增加計數,我們看到在發生16或者20次后,會進入一次broken pipe 異常
if ((err = snd_pcm_writei (playback_handle, buf, 128)) != 128) { fprintf (stderr, "write to audio interface failed (%s) %d\n",snd_strerror (err),count); //這里打印正常輸出了多少次count// snd_pcm_close (playback_handle);//snd_pcm_close (capture_handle);snd_pcm_prepare(playback_handle); // 這里恢復設備狀態// return -1;count = 0;}}period size大小的影響
/* Set period size to 32 frames. */capture_frames = 64;snd_pcm_hw_params_set_period_size_near(capture_handle,capture_params, &capture_frames, &dir);/* Write the parameters to the driver */rc = snd_pcm_hw_params(capture_handle, capture_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(capture_params, &capture_frames,&dir);capture_size = capture_frames* 4;// * 4; /* 2 bytes/sample, 2 channels */capture_buffer = (char *) malloc(capture_size);? ? ? ?在音頻的編程中,period size大小是比較重要的,也就是一次發送或者接收的幀數。
? ? ? ?設置的不合適,容易出現 underrun或者overrun的情況。通過如下的設備節點,可以看到當前采用的參數,尤其注意,查看這些參數需要音頻程序在運行時查看,否則查到的信息為設備closed
? ? ??
cat /proc/asound/card0/pcm2c/sub0/hw_params access: RW_INTERLEAVED format: S16_LE subformat: STD channels: 2 rate: 44100 (44100/1) period_size: 32 buffer_size: 1024cat /proc/asound/card0/pcm2c/sub0/sw_params tstamp_mode: NONE period_step: 1 avail_min: 32 start_threshold: 1 stop_threshold: 1024 silence_threshold: 0 silence_size: 0 boundary: 4611686018427387904? ? ? ? 此外,注意代碼中是否存在 阻塞等待的接口。例如在作為capture時,默認是阻塞的,如果沒有讀到數據,則進程會等待,導致后續發送數據的接口沒有運行,進而出現,underrun的信息。
? ? ? ? 將capture 打開時,改為非阻塞可以解決此問題,只是在處理后續的讀取操作時,要針對返回值進行異常處理? ? ?
rc = snd_pcm_open(&capture_handle, "hw:/dev/snd/controlC0" ,SND_PCM_STREAM_CAPTURE, SND_CTL_NONBLOCK); // 采用非阻塞模式,不然在下面寫的時候,堵塞導致寫的過程很慢,而INTEL HDA速率快很多,導致一直 underrun.period 時長
這個單板相關,通過如下接口進行獲取,其中第二個為輸出參數,單位為us,即period的長度
?
snd_pcm_hw_params_get_period_time() int snd_pcm_hw_params_get_period_time ( const snd_pcm_hw_params_t * params, unsigned int * val, int * dir ) Extract period time from a configuration space.Parameters params Configuration space val Returned approximate period duration in us //period的時長,以us為單位 dir Sub unit direction如果我們以此為基準進行一些計算,則要注意單板的差異。例如,筆者采用T3評估板,采用頻率設置為8K,查詢此參數為1000 000 ,即1s,也就是1s鐘才完成一次數據的發送或者接收。
參考資料
Open Sound System - ArchWiki (archlinux.org)? arch oss wiki百科信息,shell命令等的介紹都源自于此。
2)OSS v4.x API reference - The open() system call (opensound.com)OSS 編程接口。接口參數等不清楚的時候,都可以查閱此文檔。
3)ALSA 編程入門 ,這篇寫的非常詳細,優質文章。只是唯一缺點是 播放和錄音是分開的,沒有合入到一起。Introduction to Sound Programming with ALSA | Linux Journal
邊播邊放的代碼:(9條消息) alsa框架編寫應用層,實現邊播放邊錄音_aningxiaoxixi的博客-CSDN博客_alsa 邊錄邊播?5.ALSA錄放音 - 簡書 (jianshu.com)
官網簡化版本例程,短小精悍,結構清晰:?ALSA project - the C library reference: /test/pcm_min.c (alsa-project.org)?
4)ALSA wiki :?AlsaProject (alsa-project.org)
5)arch ALSA wiki:?Advanced Linux Sound Architecture - ArchWiki (archlinux.org)
6) amixer 命令行解釋及示例:?amixer: command-line mixer for ALSA soundcard driver - Linux Man Pages (1) (systutorials.com)?
7) ALSA 官方API 接口說明:?ALSA project - the C library reference: Index, Preamble and License (alsa-project.org)
8) ALSA 的設計哲學圖譜??ALSA topology - AlsaProject (alsa-project.org)
9)設備名稱:?DeviceNames - AlsaProject (alsa-project.org)?
10) mixer 命令help信息及示例??amixer: command-line mixer for ALSA soundcard driver - Linux Man Pages (1) (systutorials.com)
11) 輸入接口 Line in 和mic in 的區別:?Line In vs Mic In (Line Level Explained For Dummies) (producerhive.com)?
(9條消息) MICIN、LINEIN、LINEOUT、HPOUT、麥克風、耳機、揚聲器一次說明白_【ql君】qlexcel的博客-CSDN博客_hpout
12)硬件配圖不錯??ALSA overview - stm32mpu (stmicroelectronics.cn)?
總結
以上是生活随笔為你收集整理的Linux 音频开发之入门篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 思科计算机第七章答案,CCNA第七章 访
- 下一篇: Linux 截屏