【鼎革‧革鼎】︰ Raspbian Stretch 《六之 H.3下 》

在 Raspbian 的世界裡,一切都是『檔案』。這是真的嗎?從古早的 Unix 系統開始,就用著一種『統一』unified 的觀點來看待檔案。假如說 『程式』是執行檔很好了解,可是講到『硬體裝置』不過是特殊檔呢?這得從兩個方面來理解。首先抽象上來說,各種硬體設備可以分類成『輸入』裝置、『輸出』裝置和『輸出入』裝置。這些裝置,要嘛一個一個的作出入 ── 比如鍵盤一按輸入一個字元;滑鼠一動從一點移動到另一點 ── ;要不就一塊一塊的來讀寫 ── 就像硬碟一讀一個磁區,SD 卡一寫一個區塊。所以又被劃成了『字元』和『區塊』兩類。另一方面從操作共性上來講,檔案是可讀可寫的,可以一個一個字元的讀寫,也可以一區塊一區塊的讀寫,那為什麼不能表示不同的裝置呢?Why not!  就這樣裝置成了『特殊檔案』──裝置檔了,一般放在 Linux 檔案系統  /dev 目錄下。難道真的所有裝置都是這樣的嗎?事實上,上網用的乙太網路裝置,並不放在 /dev 目錄下,而被放在 /proc/net 目錄之下。原因正由於前面提到的『操作共性 』這一個非常重要的『類化』原則 ── 如何『比類相宜』。就像 python 的鴨子上說的一樣︰

當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子, 那麼這隻鳥就可以被稱為鴨子。

─── 《都是檔案惹的禍?

 

若講古早 Unix 之『硬體裝置檔案說』是理解 ALSA 全域觀的重要關鍵

Global view of ALSA config file framework, executive summary

The alsa-lib package (at least on Debian libasound2-data 1.0.27) provides the /usr/share/alsa/alsa.conf file as the main entry point. That file is responsible for including the full list of potential .asoundrc-format-type files on the system. It contains a reference to the ALSA “DATADIR” (Debian: /usr/share/alsa/). It continues by loading the DATADIR’s cards/aliases.conf file: that one defines translation mappings from the kernel driver’s sound card name (as listed at /proc/asound/cards, or aplay -Ll) to a “more detailed” description string. That “more detailed name” of a sound card then gets used to lookup a corresponding card-specific config file at DATADIR/cards/CARD.conf. And THAT card-specific file then attempts to provide a maximally elegant sound setup for its specific card brand, by compensating for various limitations of cards (e.g. use dmix to combat single-stream playback only, or stereo downmix to lessen a mono-output-only restriction). Finally (to support those cases where the standard setup of a soundcard is deficient/lacking, or custom plugin setup is desired), alsa.conf loads a system-global custom settings file /etc/asound.conf and a per-user custom settings file ~/.asoundrc.

So, the objective should be to achieve having the common alsa-lib configuration file framework enhanced by default in the best possible manner for each specific soundcard brand, to avoid the need of creating manually customized config files in all standard cases.

With this global overview done and cared for, let’s have a look at the actual configuration format of alsa-lib files.

……

 

,恐怕令人難以置信!

何妨仔細讀讀 alsa.conf 文本︰

pi@raspberrypi:~ more /usr/share/alsa/alsa.conf # #  ALSA library configuration file #  # pre-load the configuration files  @hooks [ 	{ 		func load 		files [ 			{ 				@func concat 				strings [ 					{ @func datadir } 					"/alsa.conf.d/" 				] 			} 			"/etc/asound.conf" 			"~/.asoundrc" 		] 		errors false 	} ] --More--(2%) </pre>    <span style="color: #666699;">想想只有兩塊聲卡︰</span> <pre class="lang:default decode:true">pi@raspberrypi:~ cat /proc/asound/cards
 0 [ALSA           ]: bcm2835 - bcm2835 ALSA
                      bcm2835 ALSA
 1 [seeed4micvoicec]: seeed-4mic-voic - seeed-4mic-voicecard
                      seeed-4mic-voicecard

 

那麼多的錄、放裝置打哪來呢?

pi@raspberrypi:~ aplay -L null     Discard all samples (playback) or generate zero samples (capture) playback dmixed ac108 default sysdefault:CARD=ALSA     bcm2835 ALSA, bcm2835 ALSA     Default Audio Device dmix:CARD=ALSA,DEV=0     bcm2835 ALSA, bcm2835 ALSA     Direct sample mixing device dmix:CARD=ALSA,DEV=1     bcm2835 ALSA, bcm2835 IEC958/HDMI     Direct sample mixing device dsnoop:CARD=ALSA,DEV=0     bcm2835 ALSA, bcm2835 ALSA     Direct sample snooping device dsnoop:CARD=ALSA,DEV=1     bcm2835 ALSA, bcm2835 IEC958/HDMI     Direct sample snooping device hw:CARD=ALSA,DEV=0     bcm2835 ALSA, bcm2835 ALSA     Direct hardware device without any conversions hw:CARD=ALSA,DEV=1     bcm2835 ALSA, bcm2835 IEC958/HDMI     Direct hardware device without any conversions plughw:CARD=ALSA,DEV=0     bcm2835 ALSA, bcm2835 ALSA     Hardware device with all software conversions plughw:CARD=ALSA,DEV=1     bcm2835 ALSA, bcm2835 IEC958/HDMI     Hardware device with all software conversions pi@raspberrypi:~ 
pi@raspberrypi:~ arecord -L null     Discard all samples (playback) or generate zero samples (capture) playback dmixed ac108 default sysdefault:CARD=seeed4micvoicec     seeed-4mic-voicecard,      Default Audio Device dmix:CARD=seeed4micvoicec,DEV=0     seeed-4mic-voicecard,      Direct sample mixing device dsnoop:CARD=seeed4micvoicec,DEV=0     seeed-4mic-voicecard,      Direct sample snooping device hw:CARD=seeed4micvoicec,DEV=0     seeed-4mic-voicecard,      Direct hardware device without any conversions plughw:CARD=seeed4micvoicec,DEV=0     seeed-4mic-voicecard,      Hardware device with all software conversions pi@raspberrypi:~ 

 

即使根本沒有聲卡︰

pi@raspberrypi:~ cat /proc/asound/cards --- no soundcards --- </pre>   <pre class="lang:default decode:true ">pi@raspberrypi:~ aplay -l
aplay: device_list:270: no soundcards found...
pi@raspberrypi:~ arecord -l arecord: device_list:270: no soundcards found... pi@raspberrypi:~ aplay -L
null
    Discard all samples (playback) or generate zero samples (capture)
default
pi@raspberrypi:~ arecord -L null     Discard all samples (playback) or generate zero samples (capture) default </pre>    <span style="color: #666699;">都還有一個稱作 null 的設備呦!</span> <h1><span style="color: #ff9900;"><a style="color: #ff9900;" href="http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html">Plugin: Null</a></span></h1> <span style="color: #808080;">This plugin discards contents of a PCM stream or creates a stream with zero samples.</span>  <span style="color: #808080;">Note: This implementation uses devices /dev/null (playback, must be writable) and /dev/full (capture, must be readable).</span> <pre class="lang:default decode:true ">pcm.name {         type null       # Null PCM } </pre> <h2><span style="color: #808080;">Function reference</span></h2> <ul>  	<li><span style="color: #808080;"><a class="el" style="color: #808080;" title="Creates a new Empty PCM." href="http://www.alsa-project.org/alsa-doc/alsa-lib/pcm__empty_8c.html#a55df6f9b1b71d37ea91557cc26e8ab3a">_snd_pcm_empty_open()</a></span></li> </ul>    <span style="color: #666699;">故知其可看成插件 Plugin 與 Soundcards 交織的裝置樹 Device Tree 也 。無怪乎有人嘗試擴張哩!?</span> <h1><span style="color: #ff9900;"><a style="color: #ff9900;" href="https://github.com/NikitaKarnauhov/lansink">LANSink</a></span></h1> <span style="color: #808080;">LANSink is simple unreliable half-duplex audio transport over UDP implemented as an ALSA plugin on sender side and as a stand-alone daemon on receiver side. It currently performs no audio compression or latency control. It is designed as a means to connect HTPC with other computers on the same network.</span>     <span style="color: #666699;">咀嚼 <a style="color: #666699;" href="https://www.alsa-project.org/main/index.php/Asoundrc">Asoundrc</a></span>  <span style="color: #666699;">A more complex tool for conversion is the pcm type plug. the syntax is:</span> <pre class="">type plug             	# Format adjusted PCM slave STR               # Slave name (see pcm_slave) # or slave {                 # Slave definition 	pcm STR         # Slave PCM name 	# or 	pcm { }         # Slave PCM definition 	[format STR]    # Slave format (default nearest) or "unchanged" 	[channels INT]  # Slave channels (default nearest) or "unchanged" 	[rate INT]      # Slave rate (default nearest) or "unchanged" } route_policy STR 	# route policy for automatic ttable generation       	                # STR can be 'default', 'average', 'copy', 'duplicate'                        # average: result is average of input channels                       	# copy: only first channels are copied to destination                       	# duplicate: duplicate first set of channels                        # default: copy policy, except for mono capture - sum ttable {               # Transfer table (bidimensional compound of                         # cchannels * schannels numbers) 	CCHANNEL { 		SCHANNEL REAL     # route value (0.0 ... 1.0) 	} }</pre> <span style="color: #808080;">We can use it as follows:</span> <pre class="">pcm_slave.sl3 { 	pcm "hw:1,0" 	format S16_LE 	channels 1 	rate 16000 }  pcm.complex_convert { 	type plug 	slave sl3 } </pre> <span style="color: #808080;">By calling it with:</span> <pre>aplay -vD complex_convert test.wav </pre> <span style="color: #808080;">You will convert the sample during playing to the sample format: S16_LE, one channel and a sample rate of 16 kHz. As you called aplay with the verbose option -v you see this options as it appears as it comes from the original file. with:</span> <pre class="">aplay -v test.wav </pre>    <span style="color: #666699;">其中味後,思 ac108 </span> <pre class="lang:default decode:true">pi@raspberrypi:~ more /etc/asound.conf 
……

pcm.ac108 {
        type ac108
        slavepcm "hw:1,0"
        channels 4       
}

 

想 mmap_emulation 難矣哉?!

The Control device

The control device for a card is the way that programs modify various “controls” on the card. For many cards this includes the mixer (but some cards, for example the rme9652, have no mixer). However, they do still have a number of other controls and some programs like JACK need to be able to access them. Examples include the digital I/O sync indicators, sample clock source switch and so on.

Aliases

With the ‘PCM hw type’ you are able to define aliases for your devices. The syntax for this definition is:

pcm.NAME {
	type hw               # Kernel PCM
	card INT/STR          # Card name or number
	[device] INT          # Device number (default 0)     
	[subdevice] INT       # Subdevice number, -1 first available (default -1)
	mmap_emulation BOOL   # enable mmap emulation for ro/wo devices
}

For example, this gives your first soundcard an alias:

pcm.primary {
	type hw
	card 0
	device 0
}

Now you can access this card by the alias ‘primary‘.

aplay -D primary test.wav