雖然 Java Applet 技術已隨瀏覽器停止支援 NPAPI 而退出歷史舞台,但其在「網頁端音訊採集與處理」上的探索極具教學價值。本文將完整複刻一個嵌入網頁的 Applet 錄音機開發過程,涵蓋音訊基礎理論、Windows API 回調採集架構、PCM 原始數據壓縮編碼策略,為理解現代 Web Audio API、MediaRecorder API 的設計哲學提供歷史鏡鑑。
在寫下第一行代碼前,必須建立正確的音訊觀念:
| 參數 | 定義 | 本專案設定值 | 選擇理由 |
|---|---|---|---|
| 採樣率 | 每秒採樣次數 (Hz) | 8 kHz | 人聲頻率集中 300-3400 Hz,奈奎斯特定理 8kHz 足夠,節省頻寬 |
| 量化位數 | 每採樣點位元數 | 16-bit | 動態範圍 96 dB,遠超 8-bit (48 dB),語音品質明顯提升 |
| 聲道數 | 單聲道/雙聲道 | 單聲道 | 語音無需立體聲,數據量減半 |
| 原始碼率 | 採樣率 × 位數 × 聲道 | 128 kbps | 8000 × 16 × 1 = 128,000 bps = 16 KB/s |
關鍵結論: 未壓縮的 PCM 數據每秒 16 KB,網頁傳輸與儲存壓力大,**壓縮編碼是剛需**。
// 1. AudioCapture - 聲音採集核心 (採用 J/Direct 調用 Windows API waveIn*)
public class AudioCapture {
// 緩衝區策略:0.1 秒/塊 = 1600 bytes (8kHz * 16bit * 1ch * 0.1s)
private static final int BUFFER_LEN = 10; // 10塊循環緩衝
private WAVEHDR[] waveHeaders;
private byte[][] buffers;
private Vector listeners = new Vector<>();
// 關鍵:回調函數機制,無需額外執行緒輪詢
private class WaveInCallback extends Callback {
public void callback(int hdrvr, int msg, int dwUser, int dw1, int dw2) {
if (msg == WIM_DATA) { // 數據就緒通知
WAVEHDR hdr = ...; // 從 dw1 取得 header
notifyListeners(hdr); // 分發事件
waveInAddBuffer(deviceId, hdr); // 重新排隊緩衝區
}
}
}
public void addAudioDataListener(AudioDataListener l) { listeners.add(l); }
private void notifyListeners(WAVEHDR hdr) {
byte[] data = extractData(hdr);
int level = calculateLevel(data); // 計算音量電平
AudioDataEvent evt = new AudioDataEvent(this, level, data);
listeners.forEach(l -> l.onAudioDataArrived(evt));
}
}
// 2. AudioDataEvent - 事件載體
public class AudioDataEvent extends EventObject {
private int level; // 當前音量電平 (用於 UI 波形/電平條)
private byte[] audioData; // 原始 PCM 數據塊
// ... getter/setter ...
}
// 3. AudioDataListener - 監聽器介面 (觀察者模式)
public interface AudioDataListener extends EventListener {
void onAudioDataArrived(AudioDataEvent evt);
}
line.read(buffer, ...),存在線程切換開銷、GC 壓力、延遲抖動。
原始 PCM 數據 (16-bit 有符號整數陣列) 必須壓縮才利於網路傳輸。作者測試對比了多種編碼:
| 編碼格式 | 壓縮比 | 音質 (語音) | 複雜度 | 授權 | 評價 |
|---|---|---|---|---|---|
| GSM 06.10 | ~6.5:1 (13 kbps) | 可接受 | 低 | 免費 | 早期手機標準,實現簡單,有現成 C 代碼可移植 |
| IMA ADPCM | 4:1 (32 kbps) | 良好 | 極低 | 免費 | 適應性差分脈碼調制,極適合語音,WAV 容器常用 |
| μ-law / A-law | 2:1 (64 kbps) | 良好 | 極低 (查表) | 免費 | 電話級壓縮,非線性量化,Java AudioFormat.Encoding 原生支援 |
| MP3 / OGG Vorbis | 8-12:1 | 極佳 | 高 | 專利/免費 | 過度殺傷,編碼延遲高,不適合即時錄音回放 |
| Speex / Opus | 可變 | 極佳 | 中 | 免費 | 現代語音編碼王者,**但當時 (2000s) 尚未成熟/普及** |
作者選擇: 文中提到測試了 GSM、IMA4、AU 等,**最終傾向 IMA ADPCM 或 GSM 06.10**,因其壓縮比可觀、算法公開免費、Java 端可純软件實現解碼播放,無需專利授權。
雖然 Applet 消亡,但其核心架構思想在現代 Web 標準中完美重生:
| Applet 時代 | 現代 Web 標準 (Web Audio API / MediaRecorder) | 核心思想傳承 |
|---|---|---|
| J/Direct 調用 waveIn* (Native) | navigator.mediaDevices.getUserMedia() + AudioContext.createMediaStreamSource() |
瀏覽器負責跨平台音訊採集,暴露統一 JS 介面 |
| Callback 機制 (WIM_DATA) | AudioWorkletProcessor / ScriptProcessorNode (onaudioprocess) |
音訊處理圖節點回調,低延遲、高優先級執行 |
| 自行實現 IMA ADPCM / GSM 編碼 | MediaRecorder (opus/vorbis/webm) / AudioEncoder (WebCodecs) |
瀏覽器內建高效編碼器,硬體加速,標準化容器 |
| Applet 沙箱權限簽名 | HTTPS 強制 + 用戶授權彈窗 (Permissions API) | 權限模型從「代碼簽名」轉移到「源站信任 + 用戶知情同意」 |
這個 Applet 錄音機專案,展示了在**無標準 API、頻寬極寶貴、跨平台極難**的 2000 年代初期,開發者如何結合 **平台特定優化 (J/Direct + 回調)**、**領域知識 (語音編碼參數選型)**、**架構模式 (觀察者模式解耦採集與編碼)** 交付產品。今天,我們享受著 MediaRecorder.start() 一行代碼搞定一切的便利,**切記這背後是無數前人踩坑、妥協、標準化的血淚歷史**。理解這段歷史,能讓我們在面對新技術 (WebCodecs, WebAssembly SIMD, WebGPU Audio) 時,具備更深層的工程判斷力。