Intro
我的目標是這樣的: 將 Raspberry Pi 2B 與 USB MIDI 鍵盤連接,造一台具備 3.5mm 音頻輸出的即插即用電子琴。
主要KPI 是: 低延遲、音頻合成穩定、支援開機自動啟動。
配置
在開始之前,先過一遍弱雞配置:
- Raspberry Pi 2B: ARM Cortex-A7 四核 900MHz, 1GB RAM
- MIDI 鍵盤: M-Audio Keystation 88 (USB 連接)
- 音頻輸出: 3.5mm 耳機插孔
- 作業系統: Raspberry Pi OS 最新版(32bit)
軟體架構
核心組件
- FluidSynth: 軟體音頻合成器
- ALSA: 底層音頻驅動系統
- SoundFont: GM 標準音色庫
- aconnect: MIDI 連接管理工具
流程是:MIDI 鍵盤 → ALSA MIDI → FluidSynth → ALSA Audio → 3.5mm 輸出
開發過程與問題解決
架構雖然簡單,但這不是我本來的想法。開始時經歷了幾個階段:
基礎設定問題
問題1: 音頻輸出選項
- 新版 Raspberry Pi OS 移除了傳統的 3.5mm 選項
- 解決方案: 使用
amixer cset numid=3 1強制 3.5mm 輸出
問題2: PulseAudio vs PipeWire
- 決策: 選擇 PulseAudio(對 Pi 2B 資源消耗較低)
延遲優化問題
問題3: 明顯的按鍵延遲
- 解決方案: 調整 FluidSynth 音頻緩衝區參數
-o audio.period-size=128 -o audio.periods=3
問題4: 和弦破音
- 原因: 音頻增益過高 + 緩衝區太小
- 解決方案: 降低增益(
-g 0.8)並平衡緩衝區大小
自動啟動問題
問題5: systemd service 權限與時序問題
- 原因: PulseAudio 連接拒絕,和音頻系統初始化時序不一致
- 我嘗試過下面的方案:
- systemd system service(失敗)
- systemd user service(部分成功)
我推測問題原因是 PulseAudio 重啟後連接失敗,根本來說是 PulseAudio 重啟太快,服務未完全就緒。
解決方案: 放棄 PulseAudio,改用 ALSA 直接輸出。
最終方案
成功配置: cron @reboot + ALSA 直接輸出
@reboot sleep 10 && /usr/local/bin/piano-startup.sh >> /home/abby/piano.log 2>&1
技術細節
FluidSynth 最佳參數
fluidsynth -a alsa -m alsa_seq -is -g 0.8 \
-o audio.period-size=128 \
-o audio.periods=3 \
-o audio.alsa.device=hw:0 \
/usr/share/sounds/sf2/FluidR3_GM.sf2 &
MIDI 連接自動化
# 等待 FluidSynth MIDI 端口就緒
timeout=10
while [ $timeout -gt 0 ]; do
if aconnect -o | grep -q "FLUID Synth"; then
break
fi
sleep 1
timeout=$((timeout-1))
done
# 自動連接 MIDI
aconnect 24:0 128:0
音頻設定
# 強制 3.5mm 輸出
amixer cset numid=3 1
amixer set Master 100%
成果
延遲 < 20ms(主觀感受良好):done。和弦演奏無破音:done。開機即可使用:done。
大成功。
效能 / Performance 方面: FluidSynth 約 15% CPU + 22% RAM,Buffer是 128 samples (2.9ms @ 44.1kHz)
一點點的 Insight
- ALSA 比 PulseAudio 更適合嵌入式應用
- systemd 不一定是最佳解決方案
- PulseAudio 增加了不必要的複雜度
GitHub
https://github.com/abbychau/Raspberry2B-midi-piano
