2025 iThome鐵人賽
「 Flutter :30天打造念佛App,跨平台從Mobile到VR,讓極樂世界在眼前實現 ! 」
Day 16
「 Flutter 語音辨識 深入應用篇 — 生活在地球的勇者啊,阿彌陀佛怎麼念呀?(1)」
昨天我們已經知道如何在flutter使用sherpa_onnx實作離線語音轉文字,
今天我們要來深入語音辨識,從App開發的角度來認識以下內容:
「音訊要先怎麼處理?」、
「如何設定模型?」、
「語音轉文字背後的完整流程」。
Day16 文章目錄:
一、音訊處理
二、設定模型
三、語音轉文字流程總覽
麥克風錄音是採用16 kHz、單聲道、PCM16,
模型要的是16 kHz、單聲道、 Float32的連續波形(約 -1.0~+1.0) ,
所以我們必須先把 PCM16(Int16)→ Float32 並做正規化後再餵進模型。
PCM16:
常見的未壓縮數位音訊格式(原始音訊),指每個取樣用 16 位元(bits)表示,
通常以整數 Int16(-32768~32767)儲存。
正規化:
將 Int16 振幅等比例縮放到 Float32的 -1.0~+1.0 範圍,例如float = int16 / 32768.0。
相較於整數Int,使用浮點數Float,能表示更細緻的能量變化,也符合後續特徵計算與模型推論需求。
Float32List convertBytesToFloat32(Uint8List bytes, [endian = Endian.little]) {
final values = Float32List(bytes.length ~/ 2);
final data = ByteData.view(bytes.buffer);
for (var i = 0; i < bytes.length; i += 2) {
int short = data.getInt16(i, endian);
values[i ~/ 2] = short / 32768.0;
}
return values;
}
Flutter sherpa_onnx ASR模型一覽
(1) 點選下載 中、英雙語模型
(2) 點選下載 純中文模型
建議:使用者第一次啟動時才下載模型,App體積會比較小
模型資料夾內部檔案,要在OnlineModelConfig 填入對應的項目名稱
備註:OnlineModel的Online 是指即時串連的語音轉文字模式,與是否網路連線無關
encoder:將語音特徵轉換成向量的聲學表徵
decoder:根據已輸出的 token 序列,預測下一個 token 是什麼。
Joiner:
把 encoder 的聲學表徵 與 decoder 的語言上下文表徵 融合,
為詞彙表中每個候選 token 輸出一個分數,供後續解碼挑選。
備註:
語言模型不是直接讀人類的中英文字詞,而是先將人類的字詞拆分成一個個小token,
透過token映射的對應ID編號進行查找。
token : 模型處理的最小單位。
token 序列 : 一段文字拆解後,轉換成一連串的 token。
模型設定流程:
createOnlineRecognizer → getModelConfigByModelName → sherpa_onnx.OnlineModelConfig
Future<sherpa_onnx.OnlineRecognizer> createOnlineRecognizer(
String modelName) async {
final type = 0;
final localModelConfig =
await getModelConfigByModelName(modelName: modelName);
final config = sherpa_onnx.OnlineRecognizerConfig(
model: localModelConfig,
ruleFsts: '',
);
return sherpa_onnx.OnlineRecognizer(config);
}
----
Future<sherpa_onnx.OnlineModelConfig> getModelConfigByModelName(
{required String modelName}) async {
final Directory directory = await getApplicationDocumentsDirectory();
final modulePath = join(directory.path, modelName);
switch (modelName) {
case "sherpa-onnx-streaming-zipformer-bilingual-zh-en-2023-02-20":
final modelDir = modulePath;
return sherpa_onnx.OnlineModelConfig(
transducer: sherpa_onnx.OnlineTransducerModelConfig(
encoder: '$modelDir/encoder-epoch-99-avg-1.int8.onnx',//聲學編碼
decoder: '$modelDir/decoder-epoch-99-avg-1.onnx',//預測器
joiner: '$modelDir/joiner-epoch-99-avg-1.onnx',//融合器
),
tokens: '$modelDir/tokens.txt',
modelType: 'zipformer',
);
default:
throw ArgumentError('Unsupported modelName: $modelName');
}
}
[麥克風輸入]
└─ 16 kHz / Mono / PCM16
│
├─ Int16 → Float32
│
[前端特徵/訊號處理](加窗、梅爾頻譜…)
│
[Encoder 聲學編碼器] → 產生高階表徵
│
│ ┌───────────────────────────┐
└──▶│ Joiner 融合器 │───▶ 輸出分數 logits → 搜尋/解碼 → token → 文字
└───────────────────────────┘
▲
│
[Prediction/Decoder 預測網路/預測器](只看已輸出 token)
1. 取樣:聲波的連續聲壓,由麥克風轉為連續電壓後,以固定頻率量測電壓(次/秒)。
例如:電話(8000 Hz )每秒量測8000次、CD音訊(44100 Hz)每秒量測44100次
我們進行語音轉文字的錄音,16kHz,就是每秒量測16000次
2. 量化:將類比聲音的波形轉換成數位。
量測到的連續幅度,會壓成有限位階(bit depth)。
例如:常見的量化位元數是16 bit ,也就是 PCM16(16位元整數)。
位元數會影響量化的精度,也就是會不會出現失真的狀況。
備註:
單聲道、雙聲道(立體聲),指的是該語音產生幾個波形。
單聲道產生一個波形,雙聲道產生兩個波形。
多數的語言模型是使用單聲道。
3. 前端特徵處理
加窗:可以將連續音訊切成一塊塊短時間片段,
讓每個片段更穩定、好分析,有助於即時串流語音辨識。
梅爾濾波器組:把線性頻率映射到更貼近人耳感受的梅爾尺度。
因為人耳對低頻更敏感、能分得更細,高頻則較不敏感。
4. 編碼Encoder、預測Decoder、融合Joiner
特徵丟進 Encoder,得到聲學表徵。
Decoder 看已輸出的 token 序列(歷史),產生語言上下文表徵。
Joiner 融合 聲學表徵 + 語言上下文表徵 ,對模型詞彙表tokens輸出分數。
5. 搜尋/解碼
解碼流程會依據各 token 的分數搜尋最可能的序列,常見策略:
Greedy:每一步挑分數最大的 token,速度快,但較容易錯過全局最佳解。
Beam search:同時保留多條高分路徑,結果較穩定但速度較慢(相較Greedy)。
找到最有可能的序列後,會將它們從編號ID→token→文字,
做必要的後處理(例如移除特殊符號)→輸出結果。
6. 端點偵測與最終結果
即時串流辨識會先產生即時的暫時結果(partial),讓畫面先更新;
當偵測到端點(停頓或語音活動結束),就把當前片段標記為最終結果(final)
並重置stream,開始下一句。
重點 | 內容 |
---|---|
音訊處理 | 轉Float32餵模型 |
模型設定 | Encoder、Decoder、Joiner |
語音轉文字流程總覽 | 基礎原理與流程 |