[Section 3] 第一個 YAML 檔案
## 什麼是 YAML 檔案
YAML 檔是一種文字檔案,非常重視縮排的特性相當類似 python 的編輯邏輯,這個規則讓編輯跟閱讀變得相當容易,但對於新手來說很容易編寫錯誤讓程式看不懂。如果你編輯完以後發現沒反應,那大概是縮排出問題了。為了減少縮排問題帶來的影響,建議你使用比較專門的編輯器,避免使用 TextEdit、Word 或是 Pages 之類的程式編輯 YAML 檔案。使用 VS Code、Sublime Text 或是 vim、 Emacs 之類的程式會讓編寫過程比較順利一些。
範例:這兩種 YAML 檔案代表的意義並不一樣
- 代碼: 選擇全部
devices:
samplerate: 44100
chunksize: 1024
capture:
type: File
filename: "dummy"
channels: 2
format: S16LE
playback:
type: File
filename: "dummy"
channels: 2
format: S16LE
- 代碼: 選擇全部
devices:
samplerate: 44100
chunksize: 1024
capture:
type: File
filename: "dummy"
channels: 2
format: S16LE
playback:
type: File
filename: "dummy"
channels: 2
format: S16LE
另外,也需要注意的是看到的 YAML 檔案內容會隨著 CamillaDSP 的版本會有些許不一樣。這篇文章依據 CamillaDSP 2.0.3 撰寫,也許 CamillaDSP 更新以後會有不一樣的寫法。如果發現問題的話,請以 GitHub 裡面的說明為準。
## CamillaDSP 需要什麼樣的訊息
根據
GitHub 的描述,CamillaDSP 會需要
* [Devices] 音樂訊號輸入與輸出
* [Resampling] 重新取樣訊號
* [Mixers] 訊號混合,有沒有需要增減數位通道
* [Filters] 訊號過濾,做各種處理。
** [Gain] 訊號增益,讓訊號變大變小
** [Volume] 整體音量控制,音控如何動作
** [Loudness] 整體響度補償,設定不同音量控制的時候如何補償高低頻響應
** [Delay] 訊號時間延遲,讓訊號晚點出現
** [FIR] 讓訊號根據載入檔案補償 FIR 曲線
** [IIR] 一般所說的 EQ,寫入各種 IIR 濾波器
** [Dither] 幫訊號增加雜訊
** [Limiter] 訊號限制器,如何處理削波 (Clipping) 的訊號
** [Difference equation] 用數學函數調整訊號響應
* [Processors] 多組數位訊號處理,目前只有壓縮器一項可以用
** [Compressor] 訊號壓縮器,可以壓縮動態範圍
* [Pipeline] 資料處理流程,照順序處理定義過的處理單元
## 一個簡單的 YAML 檔案
我用來測試 CamillaDSP 的檔案是實作一個單聲道的數位分音。將我的兩聲道 DAC 轉換為一邊是推高音,一邊推低音。
### 輸入與輸出
第一項需要的是設定輸入與輸出,這個在 devices 欄位裡面定義。要讓 CamillaDSP 處理你的音訊,你需要先將電腦的聲音輸出到安裝好的 BlackHole 裡面,換句話說就是把聲音輸出給 BlackHole,然後 CamillaDSP 從 BlackHole 截取訊號處理完丟給你的 DAC。
你會需要打開 MacOS 裡面的 "Audio MIDI Setup",裡面會有比較多的相關資訊。在這裡你可以找到 CamillaDSP 需要的內容。
你可以在裡面找到「設備名稱」、「格式」、「Sample Rate」、「channel 數量」等等的訊息。(註1)
一個好的設定會長的下面這樣:
- 代碼: 選擇全部
devices:
adjust_period: 10
capture:
change_format: false
channels: 2
device: BlackHole 16ch
format: FLOAT32LE
type: CoreAudio
capture_samplerate: 48000
chunksize: 4096
enable_rate_adjust: true
enable_resampling: true
playback:
change_format: false
channels: 2
device: 'USB Audio DAC '
exclusive: false
format: S16LE
type: CoreAudio
queuelimit: 4
rate_measure_interval: 1
resampler_type: BalancedAsync
samplerate: 48000
silence_threshold: -100
silence_timeout: 5
stop_on_rate_change: false
target_level: 0
在這份範例裡面,CamillaDSP 從 BlackHole 16ch 截取音樂,這個 BlackHole 16ch 使用了 FLOAT32LE 的音樂格式,BlackHole 16ch 擁有 2 個 channel 並且使用了 48000Hz 的 Sample Rate (capture_samplerate: 48000)。然後決定我的輸出是 "USB Audio DAC " (註1),擁有兩個 channel, 48000 Hz 的 Sample Rate (samplerate: 48000)並且使用 S16LE 格式。此外 CamillaDSP 並不會獨佔這個 DAC (exclusive: false)
註1 :如果你照著打名字還是發現 CamillaDSP 找不到 DAC,那恭喜你遇到噁心的問題了,這表示你的 DAC 製造商寫了空格在你的 DAC 名字內,所以你必須將空格也擺進 DAC 名字內。這時候 Audio MIDI Setup 程式雖然顯示了正確的名稱,但人眼無法讀取到底有幾個空格在裡面。因此你必須使用 terminal 並執行指令
- 代碼: 選擇全部
system_profiler SPAudioDataType
然後複製正確的字串。
### Filter
CamillaDSP 最重要的設定就是要對音訊做處理,每項處理都是一個 Filter 模組。每個模組都有自己的名字、種類 (type) 跟參數 (parameters)。而且你可以從範例程式碼裡面發現,定義 Filter 模組的時候並沒有定義他被用在什麼地方。這裡僅僅是定義功能,作用的範圍記載於 pipeline 區域。
這裡的定義是不分順序的,每一個 Filter 都是獨立的模組,不會互相影響。
- 代碼: 選擇全部
filters:
my_example_delay:
type: Delay
parameters:
delay: 0.019
unit: ms
subsample: false
low_pass_filter:
parameters:
freq: 2200.0
order: 4
type: ButterworthLowpass
type: BiquadCombo
high_pass_filter:
parameters:
freq: 2200.0
order: 4
type: ButterworthHighpass
type: BiquadCombo
這裡你可以看到我寫了三個 filter,延遲、高通與低通。這裡名字可以亂取,但不能重複。
### Mixer 混音器
混音器 Mixer 定義了輸入與輸出該怎麼對應的問題。想像你的音樂是一個兩聲道音樂,而 DAC 卻是八聲道的情況,這種情況顯然就不能夠一對一對應了。混音器 Mixer 就會是你需要定義的東西。
- 代碼: 選擇全部
mixers:
1x2:
channels:
in: 2
out: 2
mapping:
- dest: 0
mute: false
sources:
- channel: 0
gain: 0
inverted: false
mute: false
- dest: 1
mute: false
sources:
- channel: 0
gain: 0
inverted: false
mute: false
在這裡我的例子是兩聲道音樂僅僅使用了左聲道,然而這個左聲道音樂都被輸出到 DAC 的兩個聲道裡。這樣我可以對輸出的音樂做後續處理。
### Pipeline 資料流
Pipeline 定義了資料處理的順序,在這裡是使用列表來呈現,先定義好的東西會先被執行。而你需要知道的是,一開始的時候這個音樂是兩聲道音樂,經過了你定義的功能以後,這個音樂變成什麼了?
- 代碼: 選擇全部
pipeline:
- type: Mixer
description: 'let channel 0 occupies output channel 0 and 1'
name: 1x2
- type: Filter
channel: 0
description: 'output left channel as a tweeter'
names:
- high_pass_filter
- my_example_delay
- type: Filter
description: 'output right channel as a woofer'
channel: 1
- low_pass_filter
在這個範例裡面,一開始的兩聲道音樂先經過 (1) Mixer: 1x2,在這裡他把兩聲道音樂變成單聲道音樂,而且 channel0 跟 channel1 的聲音是一樣的。然後經過 (2) Filter,這個 Filter 是作用在 channel0 上,讓 channel0 通過高通和延遲模組。最後是 (3) Filter,這個 Filter 作用在 channel1 上,讓 channel1 通過低通模組。
*********************** 錯誤範例 ***********************
- 代碼: 選擇全部
pipeline:
- type: Filter
channel: 0
description: 'output left channel as a tweeter'
names:
- high_pass_filter
- my_example_delay
- type: Filter
description: 'output right channel as a woofer'
channel: 1
- low_pass_filter
- type: Mixer
description: 'let channel 0 occupies output channel 0 and 1'
name: 1x2
如果這份檔案的順序調換了,那麼解讀的方式會變得不一樣。在這個情況下,音樂的兩聲道會在 (1) 左聲道 channel0 通過一個高通和延遲模組,然後在 (2) 右聲道 channel1 通過一個低通模組。最後在 (3) Mixer 的部分把右聲道丟掉,讓左聲道的音樂被放進 DAC 的 channel 0 和 channel 1 中
*********************** 錯誤範例 ***********************
### 完整程式碼
這邊是完整的程式碼,你可以從這裡複製上述提到的所有程式碼,好讓你的 CamillaDSP 開始動作。不過要記得,你還是需要修改輸入與輸出才能夠讓 CamillaDSP 開始動作
- 代碼: 選擇全部
devices:
adjust_period: 10
capture:
change_format: false
channels: 2
device: BlackHole 16ch
format: FLOAT32LE
type: CoreAudio
capture_samplerate: 48000
chunksize: 4096
enable_rate_adjust: true
enable_resampling: true
playback:
change_format: false
channels: 2
device: 'USB Audio DAC '
exclusive: false
format: S16LE
type: CoreAudio
queuelimit: 4
rate_measure_interval: 1
resampler_type: BalancedAsync
samplerate: 48000
silence_threshold: -100
silence_timeout: 5
stop_on_rate_change: false
target_level: 0
filters:
my_example_delay:
type: Delay
parameters:
delay: 0.019
unit: ms
subsample: false
low_pass_filter:
parameters:
freq: 2200.0
order: 4
type: ButterworthLowpass
type: BiquadCombo
high_pass_filter:
parameters:
freq: 2200.0
order: 4
type: ButterworthHighpass
type: BiquadCombo
mixers:
1x2:
channels:
in: 2
out: 2
mapping:
- dest: 0
mute: false
sources:
- channel: 0
gain: 0
inverted: false
mute: false
- dest: 1
mute: false
sources:
- channel: 0
gain: 0
inverted: false
mute: false
pipeline:
- type: Mixer
description: 'let channel 0 occupies output channel 0 and 1'
name: 1x2
- type: Filter
channel: 0
description: 'output left channel as a tweeter'
names:
- high_pass_filter
- my_example_delay
- type: Filter
description: 'output right channel as a woofer'
channel: 1
- low_pass_filter