Use progressive W2 ONNX model in WebGPU demo
Browse files- README.md +14 -7
- SPACE_DEPLOY.md +19 -0
- app.js +491 -0
- app.py +222 -0
- requirements.txt +1 -0
- tokens.json +1031 -0
README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
| 10 |
pinned: false
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: VisualEars FastConformer FA W2 WebGPU
|
| 3 |
+
emoji: 🎙️
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.35.0
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
short_description: Browser WebGPU mic demo for VisualEars Persian ASR W2 ONNX
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# VisualEars FastConformer Persian ASR WebGPU Demo
|
| 15 |
+
|
| 16 |
+
Minimal Gradio Space for trying `Reza2kn/visualears-fastconformer-fa-full-ab-onnx-w2-progressive` directly in the browser with WebGPU.
|
| 17 |
+
|
| 18 |
+
The ONNX artifact is a fixed-frame acoustic CTC core. It expects precomputed NeMo-style 80-bin log-mel features shaped `[1, 80, 2005]`, so this Space captures mic audio in the browser, computes approximate log-mel features client-side, runs ONNX Runtime WebGPU, and greedily decodes the CTC output.
|
| 19 |
+
|
| 20 |
+
This is a rolling-window live test harness, not a true cache-aware streaming export. It is meant for quick real-life/noisy microphone experiments and latency/RTF visibility.
|
SPACE_DEPLOY.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploy
|
| 2 |
+
|
| 3 |
+
Create a new Hugging Face Space with SDK `gradio`, then upload this folder:
|
| 4 |
+
|
| 5 |
+
```bash
|
| 6 |
+
huggingface-cli repo create visualears-fastconformer-fa-webgpu --type space --space_sdk gradio
|
| 7 |
+
git clone https://huggingface.co/spaces/Reza2kn/visualears-fastconformer-fa-webgpu
|
| 8 |
+
rsync -av --delete visualears-webgpu-space/ visualears-fastconformer-fa-webgpu/
|
| 9 |
+
cd visualears-fastconformer-fa-webgpu
|
| 10 |
+
git add .
|
| 11 |
+
git commit -m "Add VisualEars WebGPU ASR demo"
|
| 12 |
+
git push
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
The app downloads the ONNX model from:
|
| 16 |
+
|
| 17 |
+
`Reza2kn/visualears-fastconformer-fa-full-ab-onnx-fp`
|
| 18 |
+
|
| 19 |
+
The first browser load fetches about 461 MB. The browser should cache later loads.
|
app.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const MODEL_URL = "https://huggingface.co/Reza2kn/visualears-fastconformer-fa-full-ab-onnx-w2-progressive/resolve/main/fastconformer_ctc_fixed2005.onnx";
|
| 2 |
+
const MODEL_DATA_URL = "https://huggingface.co/Reza2kn/visualears-fastconformer-fa-full-ab-onnx-w2-progressive/resolve/main/fastconformer_ctc_fixed2005.onnx.data";
|
| 3 |
+
const SAMPLE_RATE = 16000;
|
| 4 |
+
const N_FFT = 512;
|
| 5 |
+
const WIN_LENGTH = 400;
|
| 6 |
+
const HOP_LENGTH = 160;
|
| 7 |
+
const N_MELS = 80;
|
| 8 |
+
const FIXED_FRAMES = 2005;
|
| 9 |
+
const OUTPUT_STRIDE = 8;
|
| 10 |
+
|
| 11 |
+
const $ = (id) => document.getElementById(id);
|
| 12 |
+
let ortRuntime = null;
|
| 13 |
+
|
| 14 |
+
const state = {
|
| 15 |
+
session: null,
|
| 16 |
+
tokens: null,
|
| 17 |
+
blankId: 1024,
|
| 18 |
+
audioContext: null,
|
| 19 |
+
source: null,
|
| 20 |
+
processor: null,
|
| 21 |
+
mediaStream: null,
|
| 22 |
+
utterancePcm: new Float32Array(0),
|
| 23 |
+
finalTranscript: "",
|
| 24 |
+
partialTranscript: "",
|
| 25 |
+
speechActive: false,
|
| 26 |
+
silenceMs: 0,
|
| 27 |
+
finalizing: false,
|
| 28 |
+
recording: false,
|
| 29 |
+
decoding: false,
|
| 30 |
+
decodeTimer: null,
|
| 31 |
+
lastDecodeAt: 0,
|
| 32 |
+
fftPlan: null,
|
| 33 |
+
melFilters: null,
|
| 34 |
+
gpuName: "-",
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
function setStatus(message) {
|
| 38 |
+
$("status").textContent = message;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function setText(id, value) {
|
| 42 |
+
$(id).textContent = value;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
function formatMs(ms) {
|
| 46 |
+
if (!Number.isFinite(ms)) return "-";
|
| 47 |
+
return ms < 1000 ? `${ms.toFixed(0)} ms` : `${(ms / 1000).toFixed(2)} s`;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
function updateHeap() {
|
| 51 |
+
const mem = performance.memory;
|
| 52 |
+
if (!mem) {
|
| 53 |
+
setText("stat-heap", "unavailable");
|
| 54 |
+
return;
|
| 55 |
+
}
|
| 56 |
+
setText("stat-heap", `${(mem.usedJSHeapSize / 1048576).toFixed(0)} / ${(mem.jsHeapSizeLimit / 1048576).toFixed(0)} MB`);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
function renderTranscript() {
|
| 60 |
+
$("transcript").textContent = `Final:\n${state.finalTranscript || "..."}\n\nPartial:\n${state.partialTranscript || "..."}`;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function appendToUtterance(chunk) {
|
| 64 |
+
const maxSeconds = Number($("window-seconds")?.value || 20);
|
| 65 |
+
const maxSamples = Math.ceil(maxSeconds * SAMPLE_RATE);
|
| 66 |
+
const merged = new Float32Array(Math.min(maxSamples, state.utterancePcm.length + chunk.length));
|
| 67 |
+
const keep = Math.max(0, merged.length - chunk.length);
|
| 68 |
+
if (keep > 0) merged.set(state.utterancePcm.subarray(state.utterancePcm.length - keep), 0);
|
| 69 |
+
merged.set(chunk.subarray(Math.max(0, chunk.length - merged.length)), keep);
|
| 70 |
+
state.utterancePcm = merged;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function resampleLinear(input, fromRate, toRate) {
|
| 74 |
+
if (fromRate === toRate) return new Float32Array(input);
|
| 75 |
+
const outLen = Math.max(1, Math.round(input.length * toRate / fromRate));
|
| 76 |
+
const output = new Float32Array(outLen);
|
| 77 |
+
const ratio = (input.length - 1) / Math.max(1, outLen - 1);
|
| 78 |
+
for (let i = 0; i < outLen; i++) {
|
| 79 |
+
const x = i * ratio;
|
| 80 |
+
const j = Math.floor(x);
|
| 81 |
+
const frac = x - j;
|
| 82 |
+
output[i] = input[j] * (1 - frac) + input[Math.min(j + 1, input.length - 1)] * frac;
|
| 83 |
+
}
|
| 84 |
+
return output;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
function hzToMel(hz) {
|
| 88 |
+
return 2595 * Math.log10(1 + hz / 700);
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function melToHz(mel) {
|
| 92 |
+
return 700 * (10 ** (mel / 2595) - 1);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
function createMelFilters() {
|
| 96 |
+
const minMel = hzToMel(0);
|
| 97 |
+
const maxMel = hzToMel(SAMPLE_RATE / 2);
|
| 98 |
+
const melPoints = new Array(N_MELS + 2).fill(0).map((_, i) => minMel + (maxMel - minMel) * i / (N_MELS + 1));
|
| 99 |
+
const bins = melPoints.map((m) => Math.floor((N_FFT + 1) * melToHz(m) / SAMPLE_RATE));
|
| 100 |
+
const filters = [];
|
| 101 |
+
for (let m = 1; m <= N_MELS; m++) {
|
| 102 |
+
const filter = new Float32Array(N_FFT / 2 + 1);
|
| 103 |
+
const left = bins[m - 1], center = bins[m], right = bins[m + 1];
|
| 104 |
+
for (let k = left; k < center; k++) filter[k] = (k - left) / Math.max(1, center - left);
|
| 105 |
+
for (let k = center; k < right; k++) filter[k] = (right - k) / Math.max(1, right - center);
|
| 106 |
+
filters.push(filter);
|
| 107 |
+
}
|
| 108 |
+
return filters;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
function createFftPlan(n) {
|
| 112 |
+
const cos = new Float32Array(n / 2);
|
| 113 |
+
const sin = new Float32Array(n / 2);
|
| 114 |
+
for (let i = 0; i < n / 2; i++) {
|
| 115 |
+
cos[i] = Math.cos(-2 * Math.PI * i / n);
|
| 116 |
+
sin[i] = Math.sin(-2 * Math.PI * i / n);
|
| 117 |
+
}
|
| 118 |
+
return { n, cos, sin };
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function fftRealPower(frame, plan) {
|
| 122 |
+
const n = plan.n;
|
| 123 |
+
const re = new Float32Array(n);
|
| 124 |
+
const im = new Float32Array(n);
|
| 125 |
+
re.set(frame);
|
| 126 |
+
let j = 0;
|
| 127 |
+
for (let i = 1; i < n; i++) {
|
| 128 |
+
let bit = n >> 1;
|
| 129 |
+
for (; j & bit; bit >>= 1) j ^= bit;
|
| 130 |
+
j ^= bit;
|
| 131 |
+
if (i < j) {
|
| 132 |
+
const tr = re[i]; re[i] = re[j]; re[j] = tr;
|
| 133 |
+
const ti = im[i]; im[i] = im[j]; im[j] = ti;
|
| 134 |
+
}
|
| 135 |
+
}
|
| 136 |
+
for (let len = 2; len <= n; len <<= 1) {
|
| 137 |
+
const half = len >> 1;
|
| 138 |
+
const step = n / len;
|
| 139 |
+
for (let i = 0; i < n; i += len) {
|
| 140 |
+
for (let k = 0; k < half; k++) {
|
| 141 |
+
const idx = k * step;
|
| 142 |
+
const wr = plan.cos[idx];
|
| 143 |
+
const wi = plan.sin[idx];
|
| 144 |
+
const ur = re[i + k];
|
| 145 |
+
const ui = im[i + k];
|
| 146 |
+
const vr = re[i + k + half] * wr - im[i + k + half] * wi;
|
| 147 |
+
const vi = re[i + k + half] * wi + im[i + k + half] * wr;
|
| 148 |
+
re[i + k] = ur + vr;
|
| 149 |
+
im[i + k] = ui + vi;
|
| 150 |
+
re[i + k + half] = ur - vr;
|
| 151 |
+
im[i + k + half] = ui - vi;
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
const power = new Float32Array(n / 2 + 1);
|
| 156 |
+
for (let i = 0; i < power.length; i++) power[i] = (re[i] * re[i] + im[i] * im[i]) / n;
|
| 157 |
+
return power;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
function pcmToLogMel(pcm) {
|
| 161 |
+
if (!state.fftPlan) state.fftPlan = createFftPlan(N_FFT);
|
| 162 |
+
if (!state.melFilters) state.melFilters = createMelFilters();
|
| 163 |
+
|
| 164 |
+
const frameCount = Math.max(1, Math.min(FIXED_FRAMES, Math.floor((pcm.length - WIN_LENGTH) / HOP_LENGTH) + 1));
|
| 165 |
+
const features = new Float32Array(N_MELS * FIXED_FRAMES);
|
| 166 |
+
const hann = new Float32Array(WIN_LENGTH);
|
| 167 |
+
for (let i = 0; i < WIN_LENGTH; i++) hann[i] = 0.5 - 0.5 * Math.cos(2 * Math.PI * i / (WIN_LENGTH - 1));
|
| 168 |
+
|
| 169 |
+
for (let t = 0; t < frameCount; t++) {
|
| 170 |
+
const offset = t * HOP_LENGTH;
|
| 171 |
+
const frame = new Float32Array(N_FFT);
|
| 172 |
+
for (let i = 0; i < WIN_LENGTH; i++) frame[i] = (pcm[offset + i] || 0) * hann[i];
|
| 173 |
+
const power = fftRealPower(frame, state.fftPlan);
|
| 174 |
+
for (let m = 0; m < N_MELS; m++) {
|
| 175 |
+
const filter = state.melFilters[m];
|
| 176 |
+
let energy = 0;
|
| 177 |
+
for (let k = 0; k < filter.length; k++) energy += power[k] * filter[k];
|
| 178 |
+
features[m * FIXED_FRAMES + t] = Math.log(Math.max(energy, 1e-20));
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
for (let m = 0; m < N_MELS; m++) {
|
| 183 |
+
let mean = 0;
|
| 184 |
+
const base = m * FIXED_FRAMES;
|
| 185 |
+
for (let t = 0; t < frameCount; t++) mean += features[base + t];
|
| 186 |
+
mean /= frameCount;
|
| 187 |
+
let variance = 0;
|
| 188 |
+
for (let t = 0; t < frameCount; t++) {
|
| 189 |
+
const d = features[base + t] - mean;
|
| 190 |
+
variance += d * d;
|
| 191 |
+
}
|
| 192 |
+
const invStd = 1 / Math.sqrt(variance / frameCount + 1e-5);
|
| 193 |
+
for (let t = 0; t < frameCount; t++) features[base + t] = (features[base + t] - mean) * invStd;
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
return { features, frameCount };
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
function decodeCtc(logits, timeSteps, vocabSize) {
|
| 200 |
+
const tokens = state.tokens;
|
| 201 |
+
const blank = state.blankId;
|
| 202 |
+
let previous = -1;
|
| 203 |
+
const pieces = [];
|
| 204 |
+
for (let t = 0; t < timeSteps; t++) {
|
| 205 |
+
let best = 0;
|
| 206 |
+
let bestValue = -Infinity;
|
| 207 |
+
const base = t * vocabSize;
|
| 208 |
+
for (let i = 0; i < vocabSize; i++) {
|
| 209 |
+
const v = logits[base + i];
|
| 210 |
+
if (v > bestValue) {
|
| 211 |
+
bestValue = v;
|
| 212 |
+
best = i;
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
if (best !== blank && best !== previous && best !== 0) pieces.push(tokens[best] || "");
|
| 216 |
+
previous = best;
|
| 217 |
+
}
|
| 218 |
+
return pieces.join("").replaceAll("▁", " ").replace(/\s+/g, " ").trim();
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
async function inspectGpu() {
|
| 222 |
+
if (!navigator.gpu) {
|
| 223 |
+
state.gpuName = "not exposed";
|
| 224 |
+
setText("stat-gpu", state.gpuName);
|
| 225 |
+
return;
|
| 226 |
+
}
|
| 227 |
+
try {
|
| 228 |
+
const adapter = await navigator.gpu.requestAdapter();
|
| 229 |
+
if (!adapter) {
|
| 230 |
+
state.gpuName = "unavailable";
|
| 231 |
+
} else if (adapter.info) {
|
| 232 |
+
state.gpuName = [adapter.info.vendor, adapter.info.architecture, adapter.info.device].filter(Boolean).join(" ") || "available";
|
| 233 |
+
} else if (adapter.requestAdapterInfo) {
|
| 234 |
+
const info = await adapter.requestAdapterInfo();
|
| 235 |
+
state.gpuName = [info.vendor, info.architecture, info.device].filter(Boolean).join(" ") || "available";
|
| 236 |
+
} else {
|
| 237 |
+
state.gpuName = "available";
|
| 238 |
+
}
|
| 239 |
+
} catch (err) {
|
| 240 |
+
state.gpuName = `unavailable: ${err.message}`;
|
| 241 |
+
}
|
| 242 |
+
setText("stat-gpu", state.gpuName);
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
async function loadModel() {
|
| 246 |
+
$("load-model").disabled = true;
|
| 247 |
+
setStatus("Loading tokenizer and ONNX Runtime WebGPU...");
|
| 248 |
+
updateHeap();
|
| 249 |
+
await inspectGpu();
|
| 250 |
+
|
| 251 |
+
const tokenJson = window.VISUALEARS_TOKENS;
|
| 252 |
+
if (!tokenJson?.tokens?.length) throw new Error("Missing embedded CTC tokens");
|
| 253 |
+
state.tokens = tokenJson.tokens;
|
| 254 |
+
state.blankId = tokenJson.blank_id;
|
| 255 |
+
|
| 256 |
+
const ort = ortRuntime;
|
| 257 |
+
if (!ort) throw new Error("ONNX Runtime Web did not initialize");
|
| 258 |
+
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.26.0/dist/";
|
| 259 |
+
ort.env.wasm.numThreads = Math.max(1, Math.min(4, navigator.hardwareConcurrency || 1));
|
| 260 |
+
if (ort.env.webgpu) {
|
| 261 |
+
ort.env.webgpu.profiling = false;
|
| 262 |
+
}
|
| 263 |
+
|
| 264 |
+
const provider = $("provider").value;
|
| 265 |
+
setStatus(`Downloading/opening ${provider.toUpperCase()} session. First load is large; cached reloads should be much faster.`);
|
| 266 |
+
const start = performance.now();
|
| 267 |
+
state.session = await ort.InferenceSession.create(MODEL_URL, {
|
| 268 |
+
executionProviders: [provider],
|
| 269 |
+
externalData: [
|
| 270 |
+
{
|
| 271 |
+
path: "fastconformer_ctc_fixed2005.onnx.data",
|
| 272 |
+
data: MODEL_DATA_URL,
|
| 273 |
+
},
|
| 274 |
+
],
|
| 275 |
+
graphOptimizationLevel: "all",
|
| 276 |
+
enableMemPattern: false,
|
| 277 |
+
enableCpuMemArena: true,
|
| 278 |
+
});
|
| 279 |
+
const elapsed = performance.now() - start;
|
| 280 |
+
setText("stat-provider", provider);
|
| 281 |
+
setText("stat-decode", "-");
|
| 282 |
+
setText("stat-rtf", "-");
|
| 283 |
+
setText("stat-speed", "-");
|
| 284 |
+
updateHeap();
|
| 285 |
+
setStatus(`Model ready in ${formatMs(elapsed)}. Start the microphone when you are ready.`);
|
| 286 |
+
$("start-mic").disabled = false;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
async function startMic() {
|
| 290 |
+
if (!state.session) return;
|
| 291 |
+
state.mediaStream = await navigator.mediaDevices.getUserMedia({
|
| 292 |
+
audio: {
|
| 293 |
+
channelCount: 1,
|
| 294 |
+
echoCancellation: true,
|
| 295 |
+
noiseSuppression: true,
|
| 296 |
+
autoGainControl: true,
|
| 297 |
+
},
|
| 298 |
+
});
|
| 299 |
+
state.audioContext = new AudioContext({ sampleRate: SAMPLE_RATE });
|
| 300 |
+
state.source = state.audioContext.createMediaStreamSource(state.mediaStream);
|
| 301 |
+
state.processor = state.audioContext.createScriptProcessor(4096, 1, 1);
|
| 302 |
+
state.source.connect(state.processor);
|
| 303 |
+
state.processor.connect(state.audioContext.destination);
|
| 304 |
+
state.utterancePcm = new Float32Array(0);
|
| 305 |
+
state.finalTranscript = "";
|
| 306 |
+
state.partialTranscript = "";
|
| 307 |
+
state.speechActive = false;
|
| 308 |
+
state.silenceMs = 0;
|
| 309 |
+
state.finalizing = false;
|
| 310 |
+
renderTranscript();
|
| 311 |
+
state.recording = true;
|
| 312 |
+
$("start-mic").disabled = true;
|
| 313 |
+
$("stop-mic").disabled = false;
|
| 314 |
+
setStatus("Listening. Partial text can move while you speak; final text freezes after silence.");
|
| 315 |
+
|
| 316 |
+
state.processor.onaudioprocess = (event) => {
|
| 317 |
+
if (!state.recording) return;
|
| 318 |
+
const input = event.inputBuffer.getChannelData(0);
|
| 319 |
+
let rms = 0;
|
| 320 |
+
for (let i = 0; i < input.length; i++) rms += input[i] * input[i];
|
| 321 |
+
rms = Math.sqrt(rms / input.length);
|
| 322 |
+
$("level-bar").style.width = `${Math.min(100, rms * 900).toFixed(1)}%`;
|
| 323 |
+
const gate = Number($("noise-gate").value);
|
| 324 |
+
const speechy = rms >= gate;
|
| 325 |
+
const chunk = resampleLinear(input, state.audioContext.sampleRate, SAMPLE_RATE);
|
| 326 |
+
const chunkMs = chunk.length / SAMPLE_RATE * 1000;
|
| 327 |
+
|
| 328 |
+
if (speechy) {
|
| 329 |
+
if (!state.speechActive) {
|
| 330 |
+
state.utterancePcm = new Float32Array(0);
|
| 331 |
+
state.partialTranscript = "";
|
| 332 |
+
state.silenceMs = 0;
|
| 333 |
+
state.speechActive = true;
|
| 334 |
+
state.finalizing = false;
|
| 335 |
+
renderTranscript();
|
| 336 |
+
}
|
| 337 |
+
state.silenceMs = 0;
|
| 338 |
+
appendToUtterance(chunk);
|
| 339 |
+
return;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
if (!state.speechActive) return;
|
| 343 |
+
state.silenceMs += chunkMs;
|
| 344 |
+
if (state.silenceMs <= 350) appendToUtterance(chunk);
|
| 345 |
+
if (state.silenceMs >= 900 && !state.finalizing) {
|
| 346 |
+
state.finalizing = true;
|
| 347 |
+
finalizeUtterance();
|
| 348 |
+
}
|
| 349 |
+
};
|
| 350 |
+
|
| 351 |
+
scheduleDecode();
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
function stopMic() {
|
| 355 |
+
state.recording = false;
|
| 356 |
+
clearTimeout(state.decodeTimer);
|
| 357 |
+
state.decodeTimer = null;
|
| 358 |
+
if (state.processor) state.processor.disconnect();
|
| 359 |
+
if (state.source) state.source.disconnect();
|
| 360 |
+
if (state.mediaStream) state.mediaStream.getTracks().forEach((track) => track.stop());
|
| 361 |
+
if (state.audioContext) state.audioContext.close();
|
| 362 |
+
state.processor = null;
|
| 363 |
+
state.source = null;
|
| 364 |
+
state.mediaStream = null;
|
| 365 |
+
state.audioContext = null;
|
| 366 |
+
$("level-bar").style.width = "0%";
|
| 367 |
+
$("start-mic").disabled = !state.session;
|
| 368 |
+
$("stop-mic").disabled = true;
|
| 369 |
+
setStatus("Stopped.");
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
function scheduleDecode() {
|
| 373 |
+
clearTimeout(state.decodeTimer);
|
| 374 |
+
if (!state.recording) return;
|
| 375 |
+
const delay = Number($("decode-every").value) * 1000;
|
| 376 |
+
state.decodeTimer = setTimeout(async () => {
|
| 377 |
+
await runDecode();
|
| 378 |
+
scheduleDecode();
|
| 379 |
+
}, delay);
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
async function runDecode() {
|
| 383 |
+
if (!state.session || state.decoding || state.finalizing || !state.speechActive || state.utterancePcm.length < SAMPLE_RATE * 0.4) return;
|
| 384 |
+
await decodeUtterance(false);
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
async function finalizeUtterance() {
|
| 388 |
+
if (state.decoding) {
|
| 389 |
+
setTimeout(finalizeUtterance, 150);
|
| 390 |
+
return;
|
| 391 |
+
}
|
| 392 |
+
if (!state.session || state.utterancePcm.length < SAMPLE_RATE * 0.25) {
|
| 393 |
+
state.speechActive = false;
|
| 394 |
+
state.finalizing = false;
|
| 395 |
+
return;
|
| 396 |
+
}
|
| 397 |
+
await decodeUtterance(true);
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
async function decodeUtterance(final) {
|
| 401 |
+
state.decoding = true;
|
| 402 |
+
try {
|
| 403 |
+
const maxSeconds = Number($("window-seconds").value);
|
| 404 |
+
const samples = Math.min(state.utterancePcm.length, Math.floor(maxSeconds * SAMPLE_RATE));
|
| 405 |
+
const audio = state.utterancePcm.subarray(state.utterancePcm.length - samples);
|
| 406 |
+
const preprocessStarted = performance.now();
|
| 407 |
+
const { features, frameCount } = pcmToLogMel(audio);
|
| 408 |
+
const ort = ortRuntime;
|
| 409 |
+
if (!ort) throw new Error("ONNX Runtime Web did not initialize");
|
| 410 |
+
const tensor = new ort.Tensor("float32", features, [1, N_MELS, FIXED_FRAMES]);
|
| 411 |
+
const inferStarted = performance.now();
|
| 412 |
+
const output = await state.session.run({ processed_signal: tensor });
|
| 413 |
+
const inferElapsed = performance.now() - inferStarted;
|
| 414 |
+
const elapsed = performance.now() - preprocessStarted;
|
| 415 |
+
const logits = output.logits.data;
|
| 416 |
+
const dims = output.logits.dims;
|
| 417 |
+
const vocabSize = dims[2] || state.tokens.length;
|
| 418 |
+
const usableSteps = Math.max(1, Math.min(dims[1], Math.ceil(frameCount / OUTPUT_STRIDE)));
|
| 419 |
+
const transcript = decodeCtc(logits, usableSteps, vocabSize);
|
| 420 |
+
const audioSeconds = samples / SAMPLE_RATE;
|
| 421 |
+
const rtf = (elapsed / 1000) / Math.max(0.001, audioSeconds);
|
| 422 |
+
const speed = Math.max(0.001, audioSeconds) / Math.max(0.001, elapsed / 1000);
|
| 423 |
+
if (final) {
|
| 424 |
+
if (transcript) {
|
| 425 |
+
state.finalTranscript = state.finalTranscript ? `${state.finalTranscript}\n${transcript}` : transcript;
|
| 426 |
+
}
|
| 427 |
+
state.partialTranscript = "";
|
| 428 |
+
state.speechActive = false;
|
| 429 |
+
state.finalizing = false;
|
| 430 |
+
state.silenceMs = 0;
|
| 431 |
+
state.utterancePcm = new Float32Array(0);
|
| 432 |
+
} else {
|
| 433 |
+
state.partialTranscript = transcript;
|
| 434 |
+
}
|
| 435 |
+
renderTranscript();
|
| 436 |
+
setText("stat-decode", `${formatMs(elapsed)} total / ${formatMs(inferElapsed)} infer`);
|
| 437 |
+
setText("stat-rtf", `${rtf.toFixed(3)}`);
|
| 438 |
+
setText("stat-speed", `${speed.toFixed(1)}x`);
|
| 439 |
+
setText("stat-audio", `${audioSeconds.toFixed(1)} s`);
|
| 440 |
+
setText("stat-frames", `${frameCount} / ${FIXED_FRAMES}`);
|
| 441 |
+
setText("stat-provider", $("provider").value);
|
| 442 |
+
setText("stat-last", new Date().toLocaleTimeString());
|
| 443 |
+
updateHeap();
|
| 444 |
+
setStatus(`${final ? "Finalized" : "Decoded partial"} ${audioSeconds.toFixed(1)} s utterance with ${usableSteps} CTC steps.`);
|
| 445 |
+
} catch (err) {
|
| 446 |
+
console.error(err);
|
| 447 |
+
setStatus(`Decode failed: ${err.message}`);
|
| 448 |
+
if (final) {
|
| 449 |
+
state.speechActive = false;
|
| 450 |
+
state.finalizing = false;
|
| 451 |
+
}
|
| 452 |
+
} finally {
|
| 453 |
+
state.decoding = false;
|
| 454 |
+
}
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
async function boot() {
|
| 458 |
+
if (!$("load-model")) {
|
| 459 |
+
setTimeout(boot, 100);
|
| 460 |
+
return;
|
| 461 |
+
}
|
| 462 |
+
ortRuntime = await import("https://cdn.jsdelivr.net/npm/onnxruntime-web@1.26.0/dist/ort.webgpu.min.mjs");
|
| 463 |
+
$("load-model").addEventListener("click", () => loadModel().catch((err) => {
|
| 464 |
+
console.error(err);
|
| 465 |
+
setStatus(`Load failed: ${err.message}`);
|
| 466 |
+
$("load-model").disabled = false;
|
| 467 |
+
}));
|
| 468 |
+
$("start-mic").addEventListener("click", () => startMic().catch((err) => {
|
| 469 |
+
console.error(err);
|
| 470 |
+
setStatus(`Mic failed: ${err.message}`);
|
| 471 |
+
stopMic();
|
| 472 |
+
}));
|
| 473 |
+
$("stop-mic").addEventListener("click", stopMic);
|
| 474 |
+
$("provider").addEventListener("change", () => {
|
| 475 |
+
if (state.session) {
|
| 476 |
+
state.session = null;
|
| 477 |
+
$("start-mic").disabled = true;
|
| 478 |
+
$("load-model").disabled = false;
|
| 479 |
+
setStatus("Provider changed. Load the model again.");
|
| 480 |
+
}
|
| 481 |
+
});
|
| 482 |
+
|
| 483 |
+
setText("stat-provider", "-");
|
| 484 |
+
setText("stat-gpu", "-");
|
| 485 |
+
updateHeap();
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
boot().catch((err) => {
|
| 489 |
+
console.error(err);
|
| 490 |
+
setStatus(`Startup failed: ${err.message}`);
|
| 491 |
+
});
|
app.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pathlib import Path
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
import gradio as gr
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
ROOT = Path(__file__).parent
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def main() -> gr.Blocks:
|
| 11 |
+
tokens = json.loads((ROOT / "tokens.json").read_text(encoding="utf-8"))
|
| 12 |
+
app_js = (ROOT / "app.js").read_text(encoding="utf-8")
|
| 13 |
+
head = (
|
| 14 |
+
"<script type=\"module\">\n"
|
| 15 |
+
f"window.VISUALEARS_TOKENS = {json.dumps(tokens, ensure_ascii=False)};\n"
|
| 16 |
+
f"{app_js}\n"
|
| 17 |
+
"</script>"
|
| 18 |
+
)
|
| 19 |
+
with gr.Blocks(
|
| 20 |
+
title="VisualEars WebGPU ASR",
|
| 21 |
+
head=head,
|
| 22 |
+
css="""
|
| 23 |
+
.gradio-container { max-width: 1180px !important; }
|
| 24 |
+
#visualears-root {
|
| 25 |
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 26 |
+
color: #111827;
|
| 27 |
+
}
|
| 28 |
+
#visualears-root .topbar {
|
| 29 |
+
display: flex;
|
| 30 |
+
align-items: center;
|
| 31 |
+
justify-content: space-between;
|
| 32 |
+
gap: 16px;
|
| 33 |
+
margin-bottom: 16px;
|
| 34 |
+
}
|
| 35 |
+
#visualears-root h1 {
|
| 36 |
+
font-size: 28px;
|
| 37 |
+
line-height: 1.15;
|
| 38 |
+
margin: 0;
|
| 39 |
+
letter-spacing: 0;
|
| 40 |
+
}
|
| 41 |
+
#visualears-root .subtle {
|
| 42 |
+
color: #4b5563;
|
| 43 |
+
margin: 6px 0 0;
|
| 44 |
+
font-size: 14px;
|
| 45 |
+
}
|
| 46 |
+
#visualears-root .controls {
|
| 47 |
+
display: grid;
|
| 48 |
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 49 |
+
gap: 10px;
|
| 50 |
+
align-items: end;
|
| 51 |
+
margin: 14px 0;
|
| 52 |
+
}
|
| 53 |
+
#visualears-root button, #visualears-root select, #visualears-root input {
|
| 54 |
+
width: 100%;
|
| 55 |
+
min-height: 40px;
|
| 56 |
+
border: 1px solid #cbd5e1;
|
| 57 |
+
border-radius: 7px;
|
| 58 |
+
background: white;
|
| 59 |
+
color: #111827;
|
| 60 |
+
font: inherit;
|
| 61 |
+
padding: 8px 10px;
|
| 62 |
+
}
|
| 63 |
+
#visualears-root button.primary {
|
| 64 |
+
background: #111827;
|
| 65 |
+
color: white;
|
| 66 |
+
border-color: #111827;
|
| 67 |
+
}
|
| 68 |
+
#visualears-root button:disabled {
|
| 69 |
+
opacity: 0.45;
|
| 70 |
+
cursor: not-allowed;
|
| 71 |
+
}
|
| 72 |
+
#visualears-root label {
|
| 73 |
+
display: block;
|
| 74 |
+
font-size: 12px;
|
| 75 |
+
color: #475569;
|
| 76 |
+
margin-bottom: 5px;
|
| 77 |
+
}
|
| 78 |
+
#visualears-root .status {
|
| 79 |
+
min-height: 42px;
|
| 80 |
+
overflow-wrap: anywhere;
|
| 81 |
+
border: 1px solid #d1d5db;
|
| 82 |
+
background: #f8fafc;
|
| 83 |
+
border-radius: 7px;
|
| 84 |
+
padding: 10px 12px;
|
| 85 |
+
margin: 10px 0 14px;
|
| 86 |
+
}
|
| 87 |
+
#visualears-root .transcript {
|
| 88 |
+
min-height: 150px;
|
| 89 |
+
direction: rtl;
|
| 90 |
+
text-align: right;
|
| 91 |
+
font-size: 26px;
|
| 92 |
+
line-height: 1.65;
|
| 93 |
+
border: 1px solid #cbd5e1;
|
| 94 |
+
border-radius: 8px;
|
| 95 |
+
padding: 18px;
|
| 96 |
+
background: #ffffff;
|
| 97 |
+
overflow-wrap: anywhere;
|
| 98 |
+
white-space: pre-wrap;
|
| 99 |
+
}
|
| 100 |
+
#visualears-root .stats {
|
| 101 |
+
display: grid;
|
| 102 |
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
| 103 |
+
gap: 10px;
|
| 104 |
+
margin-top: 14px;
|
| 105 |
+
}
|
| 106 |
+
#visualears-root .stat {
|
| 107 |
+
border: 1px solid #d1d5db;
|
| 108 |
+
border-radius: 8px;
|
| 109 |
+
padding: 12px;
|
| 110 |
+
background: #ffffff;
|
| 111 |
+
min-height: 74px;
|
| 112 |
+
}
|
| 113 |
+
#visualears-root .stat b {
|
| 114 |
+
display: block;
|
| 115 |
+
font-size: 12px;
|
| 116 |
+
color: #64748b;
|
| 117 |
+
margin-bottom: 8px;
|
| 118 |
+
font-weight: 600;
|
| 119 |
+
}
|
| 120 |
+
#visualears-root .stat span {
|
| 121 |
+
font-size: 20px;
|
| 122 |
+
color: #111827;
|
| 123 |
+
overflow-wrap: anywhere;
|
| 124 |
+
}
|
| 125 |
+
#visualears-root .meter {
|
| 126 |
+
height: 8px;
|
| 127 |
+
background: #e5e7eb;
|
| 128 |
+
overflow: hidden;
|
| 129 |
+
border-radius: 999px;
|
| 130 |
+
margin-top: 8px;
|
| 131 |
+
}
|
| 132 |
+
#visualears-root .meter > div {
|
| 133 |
+
height: 100%;
|
| 134 |
+
width: 0%;
|
| 135 |
+
background: #10b981;
|
| 136 |
+
transition: width 120ms linear;
|
| 137 |
+
}
|
| 138 |
+
@media (max-width: 820px) {
|
| 139 |
+
#visualears-root .topbar { align-items: flex-start; flex-direction: column; }
|
| 140 |
+
#visualears-root .controls, #visualears-root .stats { grid-template-columns: 1fr 1fr; }
|
| 141 |
+
#visualears-root .transcript { font-size: 22px; }
|
| 142 |
+
}
|
| 143 |
+
@media (max-width: 520px) {
|
| 144 |
+
#visualears-root .controls, #visualears-root .stats { grid-template-columns: 1fr; }
|
| 145 |
+
}
|
| 146 |
+
""",
|
| 147 |
+
) as demo:
|
| 148 |
+
gr.HTML(
|
| 149 |
+
"""
|
| 150 |
+
<div id="visualears-root">
|
| 151 |
+
<div class="topbar">
|
| 152 |
+
<div>
|
| 153 |
+
<h1>VisualEars Persian ASR W2</h1>
|
| 154 |
+
<p class="subtle">Browser-only WebGPU decode with the progressive W2 QAT ONNX export.</p>
|
| 155 |
+
</div>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<div class="controls">
|
| 159 |
+
<button id="load-model" class="primary">Load WebGPU model</button>
|
| 160 |
+
<button id="start-mic" disabled>Start mic</button>
|
| 161 |
+
<button id="stop-mic" disabled>Stop</button>
|
| 162 |
+
<div>
|
| 163 |
+
<label for="decode-every">Decode every</label>
|
| 164 |
+
<select id="decode-every">
|
| 165 |
+
<option value="1.5">1.5 s</option>
|
| 166 |
+
<option value="2.5" selected>2.5 s</option>
|
| 167 |
+
<option value="4">4 s</option>
|
| 168 |
+
</select>
|
| 169 |
+
</div>
|
| 170 |
+
<div>
|
| 171 |
+
<label for="window-seconds">Max utterance</label>
|
| 172 |
+
<select id="window-seconds">
|
| 173 |
+
<option value="8">8 s</option>
|
| 174 |
+
<option value="12" selected>12 s</option>
|
| 175 |
+
<option value="18">18 s</option>
|
| 176 |
+
<option value="20">20 s</option>
|
| 177 |
+
</select>
|
| 178 |
+
</div>
|
| 179 |
+
<div>
|
| 180 |
+
<label for="provider">Provider</label>
|
| 181 |
+
<select id="provider">
|
| 182 |
+
<option value="webgpu" selected>WebGPU</option>
|
| 183 |
+
<option value="wasm">WASM fallback</option>
|
| 184 |
+
</select>
|
| 185 |
+
</div>
|
| 186 |
+
<div>
|
| 187 |
+
<label for="noise-gate">Speech gate</label>
|
| 188 |
+
<input id="noise-gate" type="range" min="0" max="0.04" value="0.006" step="0.001" />
|
| 189 |
+
</div>
|
| 190 |
+
<div>
|
| 191 |
+
<label>Input level</label>
|
| 192 |
+
<div class="meter"><div id="level-bar"></div></div>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
|
| 196 |
+
<div id="status" class="status">Idle. Load the W2 model first; the first load downloads about 461 MB and then uses browser cache.</div>
|
| 197 |
+
<div id="transcript" class="transcript">Final:
|
| 198 |
+
...
|
| 199 |
+
|
| 200 |
+
Partial:
|
| 201 |
+
...</div>
|
| 202 |
+
|
| 203 |
+
<div class="stats">
|
| 204 |
+
<div class="stat"><b>Decode Time</b><span id="stat-decode">-</span></div>
|
| 205 |
+
<div class="stat"><b>RTF</b><span id="stat-rtf">-</span></div>
|
| 206 |
+
<div class="stat"><b>Realtime Speed</b><span id="stat-speed">-</span></div>
|
| 207 |
+
<div class="stat"><b>Audio Window</b><span id="stat-audio">-</span></div>
|
| 208 |
+
<div class="stat"><b>Feature Frames</b><span id="stat-frames">-</span></div>
|
| 209 |
+
<div class="stat"><b>Provider</b><span id="stat-provider">-</span></div>
|
| 210 |
+
<div class="stat"><b>Heap Used</b><span id="stat-heap">-</span></div>
|
| 211 |
+
<div class="stat"><b>GPU Adapter</b><span id="stat-gpu">-</span></div>
|
| 212 |
+
<div class="stat"><b>Last Decode</b><span id="stat-last">-</span></div>
|
| 213 |
+
</div>
|
| 214 |
+
</div>
|
| 215 |
+
"""
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
return demo
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
if __name__ == "__main__":
|
| 222 |
+
main().launch()
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
gradio>=5.0,<6.0
|
tokens.json
ADDED
|
@@ -0,0 +1,1031 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"blank_id": 1024,
|
| 3 |
+
"unk_id": 0,
|
| 4 |
+
"tokens": [
|
| 5 |
+
"<unk>",
|
| 6 |
+
"▁ب",
|
| 7 |
+
"▁م",
|
| 8 |
+
"▁ا",
|
| 9 |
+
"▁ک",
|
| 10 |
+
"▁د",
|
| 11 |
+
"ار",
|
| 12 |
+
"ست",
|
| 13 |
+
"ان",
|
| 14 |
+
"▁ر",
|
| 15 |
+
"▁می",
|
| 16 |
+
"▁ت",
|
| 17 |
+
"ای",
|
| 18 |
+
"▁ن",
|
| 19 |
+
"▁خ",
|
| 20 |
+
"ین",
|
| 21 |
+
"▁و",
|
| 22 |
+
"▁ه",
|
| 23 |
+
"▁ش",
|
| 24 |
+
"ید",
|
| 25 |
+
"ند",
|
| 26 |
+
"▁به",
|
| 27 |
+
"رد",
|
| 28 |
+
"▁س",
|
| 29 |
+
"▁آ",
|
| 30 |
+
"▁چ",
|
| 31 |
+
"ود",
|
| 32 |
+
"▁پ",
|
| 33 |
+
"ون",
|
| 34 |
+
"▁را",
|
| 35 |
+
"▁با",
|
| 36 |
+
"▁من",
|
| 37 |
+
"اد",
|
| 38 |
+
"▁این",
|
| 39 |
+
"▁ی",
|
| 40 |
+
"▁در",
|
| 41 |
+
"یم",
|
| 42 |
+
"▁از",
|
| 43 |
+
"▁بر",
|
| 44 |
+
"▁کن",
|
| 45 |
+
"▁است",
|
| 46 |
+
"ام",
|
| 47 |
+
"▁خو",
|
| 48 |
+
"▁که",
|
| 49 |
+
"ال",
|
| 50 |
+
"اه",
|
| 51 |
+
"▁تو",
|
| 52 |
+
"▁دار",
|
| 53 |
+
"▁گ",
|
| 54 |
+
"▁ج",
|
| 55 |
+
"ور",
|
| 56 |
+
"یر",
|
| 57 |
+
"▁ف",
|
| 58 |
+
"▁ز",
|
| 59 |
+
"▁رو",
|
| 60 |
+
"▁ح",
|
| 61 |
+
"شت",
|
| 62 |
+
"فت",
|
| 63 |
+
"لی",
|
| 64 |
+
"▁هم",
|
| 65 |
+
"یا",
|
| 66 |
+
"▁او",
|
| 67 |
+
"▁ق",
|
| 68 |
+
"▁کرد",
|
| 69 |
+
"▁ع",
|
| 70 |
+
"▁یک",
|
| 71 |
+
"ما",
|
| 72 |
+
"ده",
|
| 73 |
+
"می",
|
| 74 |
+
"های",
|
| 75 |
+
"اب",
|
| 76 |
+
"▁بود",
|
| 77 |
+
"ها",
|
| 78 |
+
"ری",
|
| 79 |
+
"▁دو",
|
| 80 |
+
"لا",
|
| 81 |
+
"رو",
|
| 82 |
+
"را",
|
| 83 |
+
"جا",
|
| 84 |
+
"سی",
|
| 85 |
+
"▁آن",
|
| 86 |
+
"▁ل",
|
| 87 |
+
"کن",
|
| 88 |
+
"▁ما",
|
| 89 |
+
"▁کار",
|
| 90 |
+
"تر",
|
| 91 |
+
"▁هست",
|
| 92 |
+
"▁ص",
|
| 93 |
+
"▁چی",
|
| 94 |
+
"▁چه",
|
| 95 |
+
"زی",
|
| 96 |
+
"▁بی",
|
| 97 |
+
"▁مو",
|
| 98 |
+
"تی",
|
| 99 |
+
"▁خود",
|
| 100 |
+
"▁ام",
|
| 101 |
+
"شه",
|
| 102 |
+
"▁شما",
|
| 103 |
+
"رم",
|
| 104 |
+
"اشت",
|
| 105 |
+
"وی",
|
| 106 |
+
"▁کنم",
|
| 107 |
+
"▁نمی",
|
| 108 |
+
"▁تا",
|
| 109 |
+
"▁شد",
|
| 110 |
+
"نگ",
|
| 111 |
+
"اس",
|
| 112 |
+
"▁یه",
|
| 113 |
+
"انی",
|
| 114 |
+
"گه",
|
| 115 |
+
"فا",
|
| 116 |
+
"اید",
|
| 117 |
+
"خت",
|
| 118 |
+
"یست",
|
| 119 |
+
"▁اون",
|
| 120 |
+
"اده",
|
| 121 |
+
"اری",
|
| 122 |
+
"ول",
|
| 123 |
+
"از",
|
| 124 |
+
"ات",
|
| 125 |
+
"▁دی",
|
| 126 |
+
"وان",
|
| 127 |
+
"▁آیا",
|
| 128 |
+
"دی",
|
| 129 |
+
"▁بگ",
|
| 130 |
+
"اق",
|
| 131 |
+
"بی",
|
| 132 |
+
"اره",
|
| 133 |
+
"▁ان",
|
| 134 |
+
"ایی",
|
| 135 |
+
"▁خی",
|
| 136 |
+
"گر",
|
| 137 |
+
"ره",
|
| 138 |
+
"▁باش",
|
| 139 |
+
"گی",
|
| 140 |
+
"▁ند",
|
| 141 |
+
"مان",
|
| 142 |
+
"▁پی",
|
| 143 |
+
"خو",
|
| 144 |
+
"انه",
|
| 145 |
+
"▁هی",
|
| 146 |
+
"وم",
|
| 147 |
+
"در",
|
| 148 |
+
"اهم",
|
| 149 |
+
"▁لط",
|
| 150 |
+
"▁دست",
|
| 151 |
+
"رف",
|
| 152 |
+
"اش",
|
| 153 |
+
"▁ط",
|
| 154 |
+
"▁سر",
|
| 155 |
+
"گاه",
|
| 156 |
+
"انم",
|
| 157 |
+
"وش",
|
| 158 |
+
"▁داد",
|
| 159 |
+
"نی",
|
| 160 |
+
"▁خوب",
|
| 161 |
+
"قی",
|
| 162 |
+
"▁شده",
|
| 163 |
+
"کر",
|
| 164 |
+
"▁حال",
|
| 165 |
+
"▁ات",
|
| 166 |
+
"▁وق",
|
| 167 |
+
"▁مت",
|
| 168 |
+
"له",
|
| 169 |
+
"▁بز",
|
| 170 |
+
"▁نه",
|
| 171 |
+
"دا",
|
| 172 |
+
"▁ده",
|
| 173 |
+
"▁بب",
|
| 174 |
+
"▁بد",
|
| 175 |
+
"▁پر",
|
| 176 |
+
"▁هیچ",
|
| 177 |
+
"ونه",
|
| 178 |
+
"بت",
|
| 179 |
+
"نده",
|
| 180 |
+
"▁گفت",
|
| 181 |
+
"شک",
|
| 182 |
+
"▁تر",
|
| 183 |
+
"▁غ",
|
| 184 |
+
"▁پس",
|
| 185 |
+
"▁مح",
|
| 186 |
+
"▁همه",
|
| 187 |
+
"شم",
|
| 188 |
+
"▁ال",
|
| 189 |
+
"وه",
|
| 190 |
+
"عی",
|
| 191 |
+
"▁هر",
|
| 192 |
+
"▁رفت",
|
| 193 |
+
"▁بع",
|
| 194 |
+
"▁سا",
|
| 195 |
+
"▁کم",
|
| 196 |
+
"▁مع",
|
| 197 |
+
"فر",
|
| 198 |
+
"▁گر",
|
| 199 |
+
"▁چند",
|
| 200 |
+
"▁ای",
|
| 201 |
+
"▁کجا",
|
| 202 |
+
"است",
|
| 203 |
+
"▁میک",
|
| 204 |
+
"▁بخ",
|
| 205 |
+
"▁اس",
|
| 206 |
+
"▁مرد",
|
| 207 |
+
"▁یا",
|
| 208 |
+
"دم",
|
| 209 |
+
"توان",
|
| 210 |
+
"▁روز",
|
| 211 |
+
"به",
|
| 212 |
+
"شن",
|
| 213 |
+
"▁ها",
|
| 214 |
+
"عت",
|
| 215 |
+
"▁فر",
|
| 216 |
+
"وری",
|
| 217 |
+
"کت",
|
| 218 |
+
"که",
|
| 219 |
+
"▁فکر",
|
| 220 |
+
"▁کش",
|
| 221 |
+
"چه",
|
| 222 |
+
"نج",
|
| 223 |
+
"▁نش",
|
| 224 |
+
"▁باز",
|
| 225 |
+
"▁مر",
|
| 226 |
+
"جود",
|
| 227 |
+
"کی",
|
| 228 |
+
"وز",
|
| 229 |
+
"▁سال",
|
| 230 |
+
"ارم",
|
| 231 |
+
"لم",
|
| 232 |
+
"یده",
|
| 233 |
+
"▁بده",
|
| 234 |
+
"▁راه",
|
| 235 |
+
"اند",
|
| 236 |
+
"بر",
|
| 237 |
+
"امه",
|
| 238 |
+
"الی",
|
| 239 |
+
"تون",
|
| 240 |
+
"هم",
|
| 241 |
+
"ارد",
|
| 242 |
+
"▁آم",
|
| 243 |
+
"▁چرا",
|
| 244 |
+
"▁مد",
|
| 245 |
+
"طور",
|
| 246 |
+
"▁های",
|
| 247 |
+
"▁میش",
|
| 248 |
+
"▁کند",
|
| 249 |
+
"▁تم",
|
| 250 |
+
"کار",
|
| 251 |
+
"اط",
|
| 252 |
+
"▁زند",
|
| 253 |
+
"▁میگ",
|
| 254 |
+
"▁اما",
|
| 255 |
+
"قه",
|
| 256 |
+
"▁روی",
|
| 257 |
+
"▁زی",
|
| 258 |
+
"▁مس",
|
| 259 |
+
"لو",
|
| 260 |
+
"زه",
|
| 261 |
+
"▁اح",
|
| 262 |
+
"▁رس",
|
| 263 |
+
"▁واق",
|
| 264 |
+
"▁قر",
|
| 265 |
+
"▁کسی",
|
| 266 |
+
"هر",
|
| 267 |
+
"فی",
|
| 268 |
+
"▁کنی",
|
| 269 |
+
"ادر",
|
| 270 |
+
"یاز",
|
| 271 |
+
"▁کی",
|
| 272 |
+
"▁پیش",
|
| 273 |
+
"ونی",
|
| 274 |
+
"▁حرف",
|
| 275 |
+
"▁خوش",
|
| 276 |
+
"▁نب",
|
| 277 |
+
"▁شر",
|
| 278 |
+
"قدر",
|
| 279 |
+
"جه",
|
| 280 |
+
"▁مید",
|
| 281 |
+
"عه",
|
| 282 |
+
"سته",
|
| 283 |
+
"شین",
|
| 284 |
+
"ونم",
|
| 285 |
+
"▁نم",
|
| 286 |
+
"ادی",
|
| 287 |
+
"وت",
|
| 288 |
+
"▁حس",
|
| 289 |
+
"عا",
|
| 290 |
+
"▁مم",
|
| 291 |
+
"ظر",
|
| 292 |
+
"▁نو",
|
| 293 |
+
"سه",
|
| 294 |
+
"▁بعد",
|
| 295 |
+
"فته",
|
| 296 |
+
"انید",
|
| 297 |
+
"▁بین",
|
| 298 |
+
"▁ار",
|
| 299 |
+
"▁سو",
|
| 300 |
+
"▁سی",
|
| 301 |
+
"یدم",
|
| 302 |
+
"▁عم",
|
| 303 |
+
"باره",
|
| 304 |
+
"اهد",
|
| 305 |
+
"▁بار",
|
| 306 |
+
"▁شن",
|
| 307 |
+
"اشته",
|
| 308 |
+
"یرم",
|
| 309 |
+
"▁تع",
|
| 310 |
+
"مین",
|
| 311 |
+
"▁تح",
|
| 312 |
+
"اهی",
|
| 313 |
+
"▁دن",
|
| 314 |
+
"▁پنج",
|
| 315 |
+
"بال",
|
| 316 |
+
"▁زن",
|
| 317 |
+
"▁دور",
|
| 318 |
+
"▁ولی",
|
| 319 |
+
"شتر",
|
| 320 |
+
"شون",
|
| 321 |
+
"رگ",
|
| 322 |
+
"زار",
|
| 323 |
+
"▁تن",
|
| 324 |
+
"▁یکی",
|
| 325 |
+
"صی",
|
| 326 |
+
"با",
|
| 327 |
+
"▁صد",
|
| 328 |
+
"▁شود",
|
| 329 |
+
"▁مث",
|
| 330 |
+
"قط",
|
| 331 |
+
"▁آب",
|
| 332 |
+
"وا",
|
| 333 |
+
"▁گذ",
|
| 334 |
+
"▁چیز",
|
| 335 |
+
"▁نز",
|
| 336 |
+
"▁اق",
|
| 337 |
+
"▁بسی",
|
| 338 |
+
"▁ری",
|
| 339 |
+
"▁وقت",
|
| 340 |
+
"سم",
|
| 341 |
+
"ترین",
|
| 342 |
+
"▁اد",
|
| 343 |
+
"زم",
|
| 344 |
+
"یدن",
|
| 345 |
+
"اف",
|
| 346 |
+
"▁اص",
|
| 347 |
+
"▁پای",
|
| 348 |
+
"▁قب",
|
| 349 |
+
"▁خر",
|
| 350 |
+
"▁دان",
|
| 351 |
+
"▁شک",
|
| 352 |
+
"رده",
|
| 353 |
+
"یری",
|
| 354 |
+
"ردم",
|
| 355 |
+
"▁مق",
|
| 356 |
+
"▁فقط",
|
| 357 |
+
"▁اند",
|
| 358 |
+
"▁نی",
|
| 359 |
+
"نامه",
|
| 360 |
+
"جام",
|
| 361 |
+
"دید",
|
| 362 |
+
"وب",
|
| 363 |
+
"کنم",
|
| 364 |
+
"▁کت",
|
| 365 |
+
"نه",
|
| 366 |
+
"▁میز",
|
| 367 |
+
"اک",
|
| 368 |
+
"▁اگه",
|
| 369 |
+
"▁بن",
|
| 370 |
+
"▁فی",
|
| 371 |
+
"▁صح",
|
| 372 |
+
"مد",
|
| 373 |
+
"▁مرا",
|
| 374 |
+
"▁بچه",
|
| 375 |
+
"شان",
|
| 376 |
+
"شی",
|
| 377 |
+
"اح",
|
| 378 |
+
"▁یاد",
|
| 379 |
+
"وس",
|
| 380 |
+
"▁جه",
|
| 381 |
+
"▁کمی",
|
| 382 |
+
"▁اول",
|
| 383 |
+
"وند",
|
| 384 |
+
"▁سه",
|
| 385 |
+
"▁نظر",
|
| 386 |
+
"نم",
|
| 387 |
+
"وق",
|
| 388 |
+
"اخت",
|
| 389 |
+
"▁بل",
|
| 390 |
+
"کان",
|
| 391 |
+
"▁خدا",
|
| 392 |
+
"ایه",
|
| 393 |
+
"هایی",
|
| 394 |
+
"تم",
|
| 395 |
+
"جی",
|
| 396 |
+
"▁بیا",
|
| 397 |
+
"▁لب",
|
| 398 |
+
"روز",
|
| 399 |
+
"▁اگر",
|
| 400 |
+
"▁برد",
|
| 401 |
+
"▁نق",
|
| 402 |
+
"▁هن",
|
| 403 |
+
"▁بیم",
|
| 404 |
+
"ستان",
|
| 405 |
+
"▁جم",
|
| 406 |
+
"▁زد",
|
| 407 |
+
"▁پول",
|
| 408 |
+
"▁موق",
|
| 409 |
+
"کس",
|
| 410 |
+
"خی",
|
| 411 |
+
"▁شم",
|
| 412 |
+
"▁پید",
|
| 413 |
+
"ضی",
|
| 414 |
+
"▁ساز",
|
| 415 |
+
"ینم",
|
| 416 |
+
"بان",
|
| 417 |
+
"▁تص",
|
| 418 |
+
"▁شب",
|
| 419 |
+
"▁افت",
|
| 420 |
+
"ویی",
|
| 421 |
+
"▁دل",
|
| 422 |
+
"▁جو",
|
| 423 |
+
"لام",
|
| 424 |
+
"▁تل",
|
| 425 |
+
"▁بیر",
|
| 426 |
+
"▁انت",
|
| 427 |
+
"ختر",
|
| 428 |
+
"هی",
|
| 429 |
+
"▁ایر",
|
| 430 |
+
"▁اه",
|
| 431 |
+
"▁تق",
|
| 432 |
+
"▁برو",
|
| 433 |
+
"▁خب",
|
| 434 |
+
"ایش",
|
| 435 |
+
"▁پرو",
|
| 436 |
+
"▁کو",
|
| 437 |
+
"▁اط",
|
| 438 |
+
"ورد",
|
| 439 |
+
"▁اخ",
|
| 440 |
+
"بار",
|
| 441 |
+
"▁کمک",
|
| 442 |
+
"▁برگ",
|
| 443 |
+
"اخ",
|
| 444 |
+
"▁انگ",
|
| 445 |
+
"▁کام",
|
| 446 |
+
"ینی",
|
| 447 |
+
"شید",
|
| 448 |
+
"▁مج",
|
| 449 |
+
"حال",
|
| 450 |
+
"ریک",
|
| 451 |
+
"▁شهر",
|
| 452 |
+
"▁فرو",
|
| 453 |
+
"▁سف",
|
| 454 |
+
"ذار",
|
| 455 |
+
"▁مثل",
|
| 456 |
+
"▁نام",
|
| 457 |
+
"▁کنه",
|
| 458 |
+
"▁مش",
|
| 459 |
+
"فاده",
|
| 460 |
+
"▁توی",
|
| 461 |
+
"ینه",
|
| 462 |
+
"غی",
|
| 463 |
+
"▁شدن",
|
| 464 |
+
"▁چون",
|
| 465 |
+
"ایت",
|
| 466 |
+
"▁پسر",
|
| 467 |
+
"▁میخ",
|
| 468 |
+
"مت",
|
| 469 |
+
"اشتم",
|
| 470 |
+
"یشه",
|
| 471 |
+
"دیک",
|
| 472 |
+
"▁گوش",
|
| 473 |
+
"▁اف",
|
| 474 |
+
"▁قط",
|
| 475 |
+
"▁خان",
|
| 476 |
+
"اسه",
|
| 477 |
+
"ارت",
|
| 478 |
+
"اطر",
|
| 479 |
+
"یره",
|
| 480 |
+
"ستم",
|
| 481 |
+
"▁لا",
|
| 482 |
+
"▁ض",
|
| 483 |
+
"▁تغی",
|
| 484 |
+
"ورت",
|
| 485 |
+
"▁جن",
|
| 486 |
+
"کنه",
|
| 487 |
+
"▁دقی",
|
| 488 |
+
"▁نک",
|
| 489 |
+
"▁گل",
|
| 490 |
+
"دام",
|
| 491 |
+
"▁احس",
|
| 492 |
+
"▁مشک",
|
| 493 |
+
"اهید",
|
| 494 |
+
"یدی",
|
| 495 |
+
"▁شو",
|
| 496 |
+
"تو",
|
| 497 |
+
"▁بله",
|
| 498 |
+
"کنی",
|
| 499 |
+
"اپ",
|
| 500 |
+
"جر",
|
| 501 |
+
"ندی",
|
| 502 |
+
"لات",
|
| 503 |
+
"مون",
|
| 504 |
+
"▁نس",
|
| 505 |
+
"▁مه",
|
| 506 |
+
"▁کل",
|
| 507 |
+
"لاق",
|
| 508 |
+
"▁میر",
|
| 509 |
+
"▁جای",
|
| 510 |
+
"▁شرو",
|
| 511 |
+
"▁تب",
|
| 512 |
+
"▁مهم",
|
| 513 |
+
"تان",
|
| 514 |
+
"انند",
|
| 515 |
+
"اسی",
|
| 516 |
+
"ازه",
|
| 517 |
+
"▁مخ",
|
| 518 |
+
"▁زیر",
|
| 519 |
+
"▁وی",
|
| 520 |
+
"▁آر",
|
| 521 |
+
"وهر",
|
| 522 |
+
"▁زود",
|
| 523 |
+
"▁تما",
|
| 524 |
+
"▁رنگ",
|
| 525 |
+
"▁صب",
|
| 526 |
+
"گان",
|
| 527 |
+
"▁اب",
|
| 528 |
+
"ارش",
|
| 529 |
+
"▁غذ",
|
| 530 |
+
"▁خط",
|
| 531 |
+
"عیت",
|
| 532 |
+
"▁بش",
|
| 533 |
+
"▁کوچ",
|
| 534 |
+
"شته",
|
| 535 |
+
"▁شه",
|
| 536 |
+
"ابر",
|
| 537 |
+
"▁طول",
|
| 538 |
+
"▁پار",
|
| 539 |
+
"▁بهش",
|
| 540 |
+
"▁درد",
|
| 541 |
+
"این",
|
| 542 |
+
"▁مط",
|
| 543 |
+
"▁همچ",
|
| 544 |
+
"خته",
|
| 545 |
+
"▁اشت",
|
| 546 |
+
"اسف",
|
| 547 |
+
"▁میب",
|
| 548 |
+
"▁صبح",
|
| 549 |
+
"▁ذ",
|
| 550 |
+
"مو",
|
| 551 |
+
"ورم",
|
| 552 |
+
"▁نگ",
|
| 553 |
+
"▁سرم",
|
| 554 |
+
"▁پشت",
|
| 555 |
+
"▁لی",
|
| 556 |
+
"▁ور",
|
| 557 |
+
"شگاه",
|
| 558 |
+
"گری",
|
| 559 |
+
"اص",
|
| 560 |
+
"▁چشم",
|
| 561 |
+
"فه",
|
| 562 |
+
"▁پرس",
|
| 563 |
+
"▁بپ",
|
| 564 |
+
"▁برم",
|
| 565 |
+
"وع",
|
| 566 |
+
"ظه",
|
| 567 |
+
"کنند",
|
| 568 |
+
"▁اعت",
|
| 569 |
+
"ابل",
|
| 570 |
+
"▁اع",
|
| 571 |
+
"یستم",
|
| 572 |
+
"▁شیر",
|
| 573 |
+
"▁شدم",
|
| 574 |
+
"▁خار",
|
| 575 |
+
"▁امت",
|
| 576 |
+
"▁فرد",
|
| 577 |
+
"▁خون",
|
| 578 |
+
"مل",
|
| 579 |
+
"▁آخ",
|
| 580 |
+
"فتم",
|
| 581 |
+
"▁سب",
|
| 582 |
+
"امی",
|
| 583 |
+
"شب",
|
| 584 |
+
"لت",
|
| 585 |
+
"طی",
|
| 586 |
+
"▁حق",
|
| 587 |
+
"ندان",
|
| 588 |
+
"▁منو",
|
| 589 |
+
"▁جل",
|
| 590 |
+
"سر",
|
| 591 |
+
"یات",
|
| 592 |
+
"بانی",
|
| 593 |
+
"▁جا",
|
| 594 |
+
"شو",
|
| 595 |
+
"دار",
|
| 596 |
+
"▁مدر",
|
| 597 |
+
"▁پرد",
|
| 598 |
+
"▁بلی",
|
| 599 |
+
"نین",
|
| 600 |
+
"هاش",
|
| 601 |
+
"▁اش",
|
| 602 |
+
"ابی",
|
| 603 |
+
"▁جان",
|
| 604 |
+
"▁اج",
|
| 605 |
+
"▁کا",
|
| 606 |
+
"▁خبر",
|
| 607 |
+
"▁قد",
|
| 608 |
+
"▁هفت",
|
| 609 |
+
"▁تیم",
|
| 610 |
+
"▁توس",
|
| 611 |
+
"▁مست",
|
| 612 |
+
"▁ماه",
|
| 613 |
+
"لیس",
|
| 614 |
+
"▁شام",
|
| 615 |
+
"ایید",
|
| 616 |
+
"گران",
|
| 617 |
+
"▁گو",
|
| 618 |
+
"▁چیه",
|
| 619 |
+
"▁ث",
|
| 620 |
+
"▁کس",
|
| 621 |
+
"▁کر",
|
| 622 |
+
"▁گم",
|
| 623 |
+
"▁آدم",
|
| 624 |
+
"▁آق",
|
| 625 |
+
"اسفم",
|
| 626 |
+
"▁بهم",
|
| 627 |
+
"▁منم",
|
| 628 |
+
"▁منت",
|
| 629 |
+
"اشق",
|
| 630 |
+
"کنیم",
|
| 631 |
+
"▁بشه",
|
| 632 |
+
"▁عمل",
|
| 633 |
+
"▁مال",
|
| 634 |
+
"سان",
|
| 635 |
+
"▁بگو",
|
| 636 |
+
"▁ارز",
|
| 637 |
+
"▁آره",
|
| 638 |
+
"تب",
|
| 639 |
+
"یس",
|
| 640 |
+
"مه",
|
| 641 |
+
"ذر",
|
| 642 |
+
"ستی",
|
| 643 |
+
"ایم",
|
| 644 |
+
"▁اجا",
|
| 645 |
+
"شار",
|
| 646 |
+
"یمی",
|
| 647 |
+
"▁حر",
|
| 648 |
+
"▁مص",
|
| 649 |
+
"ودی",
|
| 650 |
+
"▁مام",
|
| 651 |
+
"فتن",
|
| 652 |
+
"دهد",
|
| 653 |
+
"رسی",
|
| 654 |
+
"▁نفر",
|
| 655 |
+
"فاق",
|
| 656 |
+
"▁خد",
|
| 657 |
+
"▁هوا",
|
| 658 |
+
"▁طرف",
|
| 659 |
+
"▁نگه",
|
| 660 |
+
"تا",
|
| 661 |
+
"▁پدر",
|
| 662 |
+
"▁نف",
|
| 663 |
+
"▁ببخ",
|
| 664 |
+
"▁افر",
|
| 665 |
+
"گار",
|
| 666 |
+
"ژی",
|
| 667 |
+
"▁نکن",
|
| 668 |
+
"▁بزن",
|
| 669 |
+
"▁گرد",
|
| 670 |
+
"▁سنگ",
|
| 671 |
+
"▁بهت",
|
| 672 |
+
"خص",
|
| 673 |
+
"غل",
|
| 674 |
+
"▁جنگ",
|
| 675 |
+
"لوم",
|
| 676 |
+
"نا",
|
| 677 |
+
"▁عرو",
|
| 678 |
+
"▁پیر",
|
| 679 |
+
"کرد",
|
| 680 |
+
"▁قه",
|
| 681 |
+
"▁صند",
|
| 682 |
+
"▁هشت",
|
| 683 |
+
"ایل",
|
| 684 |
+
"▁عو",
|
| 685 |
+
"▁بک",
|
| 686 |
+
"▁اسم",
|
| 687 |
+
"▁کود",
|
| 688 |
+
"شد",
|
| 689 |
+
"کتر",
|
| 690 |
+
"زشک",
|
| 691 |
+
"▁بگم",
|
| 692 |
+
"نوان",
|
| 693 |
+
"لاع",
|
| 694 |
+
"▁علا",
|
| 695 |
+
"اهند",
|
| 696 |
+
"▁ترس",
|
| 697 |
+
"▁غیر",
|
| 698 |
+
"بون",
|
| 699 |
+
"ینده",
|
| 700 |
+
"صت",
|
| 701 |
+
"پی",
|
| 702 |
+
"▁ظ",
|
| 703 |
+
"ضا",
|
| 704 |
+
"حت",
|
| 705 |
+
"▁لح",
|
| 706 |
+
"نش",
|
| 707 |
+
"▁هز",
|
| 708 |
+
"▁جام",
|
| 709 |
+
"▁آز",
|
| 710 |
+
"▁حتی",
|
| 711 |
+
"▁شی",
|
| 712 |
+
"ویس",
|
| 713 |
+
"▁سپ",
|
| 714 |
+
"▁حم",
|
| 715 |
+
"▁حا",
|
| 716 |
+
"نت",
|
| 717 |
+
"عتی",
|
| 718 |
+
"▁ممن",
|
| 719 |
+
"میم",
|
| 720 |
+
"▁زنگ",
|
| 721 |
+
"▁تلف",
|
| 722 |
+
"▁نظ",
|
| 723 |
+
"داشت",
|
| 724 |
+
"▁موف",
|
| 725 |
+
"▁مدت",
|
| 726 |
+
"▁وا",
|
| 727 |
+
"▁دهم",
|
| 728 |
+
"زد",
|
| 729 |
+
"▁آه",
|
| 730 |
+
"▁قبل",
|
| 731 |
+
"▁گی",
|
| 732 |
+
"▁پا",
|
| 733 |
+
"▁بخو",
|
| 734 |
+
"▁کلی",
|
| 735 |
+
"▁بال",
|
| 736 |
+
"رس",
|
| 737 |
+
"▁خیا",
|
| 738 |
+
"قب",
|
| 739 |
+
"▁دیر",
|
| 740 |
+
"▁رود",
|
| 741 |
+
"▁نار",
|
| 742 |
+
"▁سخ",
|
| 743 |
+
"▁بخش",
|
| 744 |
+
"رین",
|
| 745 |
+
"یمت",
|
| 746 |
+
"خانه",
|
| 747 |
+
"▁منظ",
|
| 748 |
+
"ثر",
|
| 749 |
+
"تما",
|
| 750 |
+
"حان",
|
| 751 |
+
"قت",
|
| 752 |
+
"جان",
|
| 753 |
+
"شنه",
|
| 754 |
+
"▁حد",
|
| 755 |
+
"▁عکس",
|
| 756 |
+
"▁بدی",
|
| 757 |
+
"میر",
|
| 758 |
+
"▁قدر",
|
| 759 |
+
"ابه",
|
| 760 |
+
"▁ژ",
|
| 761 |
+
"▁جلو",
|
| 762 |
+
"▁مگه",
|
| 763 |
+
"یرید",
|
| 764 |
+
"بیه",
|
| 765 |
+
"▁سری",
|
| 766 |
+
"▁هو",
|
| 767 |
+
"▁سفر",
|
| 768 |
+
"مانی",
|
| 769 |
+
"ندم",
|
| 770 |
+
"صد",
|
| 771 |
+
"▁آش",
|
| 772 |
+
"▁غذا",
|
| 773 |
+
"▁جمع",
|
| 774 |
+
"▁درو",
|
| 775 |
+
"رات",
|
| 776 |
+
"▁بس",
|
| 777 |
+
"▁بید",
|
| 778 |
+
"▁کاش",
|
| 779 |
+
"▁ست",
|
| 780 |
+
"▁جور",
|
| 781 |
+
"▁شلو",
|
| 782 |
+
"▁آس",
|
| 783 |
+
"کسی",
|
| 784 |
+
"▁بح",
|
| 785 |
+
"استم",
|
| 786 |
+
"▁دلم",
|
| 787 |
+
"▁سخت",
|
| 788 |
+
"تونم",
|
| 789 |
+
"رخ",
|
| 790 |
+
"▁باب",
|
| 791 |
+
"میز",
|
| 792 |
+
"ثیر",
|
| 793 |
+
"▁میآ",
|
| 794 |
+
"عنی",
|
| 795 |
+
"باه",
|
| 796 |
+
"▁پاس",
|
| 797 |
+
"اله",
|
| 798 |
+
"▁موض",
|
| 799 |
+
"▁نا",
|
| 800 |
+
"لیل",
|
| 801 |
+
"اشتن",
|
| 802 |
+
"زارش",
|
| 803 |
+
"▁سمت",
|
| 804 |
+
"▁کف",
|
| 805 |
+
"▁عزی",
|
| 806 |
+
"▁تش",
|
| 807 |
+
"▁بست",
|
| 808 |
+
"▁صدا",
|
| 809 |
+
"▁تخ",
|
| 810 |
+
"▁نخ",
|
| 811 |
+
"▁ایج",
|
| 812 |
+
"▁زدن",
|
| 813 |
+
"یمان",
|
| 814 |
+
"▁اوه",
|
| 815 |
+
"▁فهم",
|
| 816 |
+
"▁اوم",
|
| 817 |
+
"▁دم",
|
| 818 |
+
"▁مسا",
|
| 819 |
+
"ارو",
|
| 820 |
+
"▁جون",
|
| 821 |
+
"وار",
|
| 822 |
+
"اهیم",
|
| 823 |
+
"داد",
|
| 824 |
+
"▁گرو",
|
| 825 |
+
"جوری",
|
| 826 |
+
"▁مسئ",
|
| 827 |
+
"ردی",
|
| 828 |
+
"▁عص",
|
| 829 |
+
"▁رق",
|
| 830 |
+
"▁عوض",
|
| 831 |
+
"حی",
|
| 832 |
+
"▁پوش",
|
| 833 |
+
"▁حل",
|
| 834 |
+
"▁کشت",
|
| 835 |
+
"اهر",
|
| 836 |
+
"▁رز",
|
| 837 |
+
"داری",
|
| 838 |
+
"شتری",
|
| 839 |
+
"صو",
|
| 840 |
+
"▁درس",
|
| 841 |
+
"▁زیب",
|
| 842 |
+
"ریبا",
|
| 843 |
+
"▁روش",
|
| 844 |
+
"وده",
|
| 845 |
+
"اتی",
|
| 846 |
+
"▁تان",
|
| 847 |
+
"کا",
|
| 848 |
+
"طه",
|
| 849 |
+
"خاب",
|
| 850 |
+
"▁پو",
|
| 851 |
+
"واده",
|
| 852 |
+
"واج",
|
| 853 |
+
"دن",
|
| 854 |
+
"▁دید",
|
| 855 |
+
"▁اقا",
|
| 856 |
+
"▁مرگ",
|
| 857 |
+
"▁وس",
|
| 858 |
+
"▁حو",
|
| 859 |
+
"سال",
|
| 860 |
+
"وشید",
|
| 861 |
+
"▁شش",
|
| 862 |
+
"نو",
|
| 863 |
+
"▁محی",
|
| 864 |
+
"راک",
|
| 865 |
+
"▁اسب",
|
| 866 |
+
"▁قسم",
|
| 867 |
+
"کز",
|
| 868 |
+
"▁تجر",
|
| 869 |
+
"▁بچ",
|
| 870 |
+
"▁بفر",
|
| 871 |
+
"تش",
|
| 872 |
+
"گونه",
|
| 873 |
+
"▁اید",
|
| 874 |
+
"نگی",
|
| 875 |
+
"▁دا",
|
| 876 |
+
"خره",
|
| 877 |
+
"▁دع",
|
| 878 |
+
"▁ازد",
|
| 879 |
+
"▁کوت",
|
| 880 |
+
"انت",
|
| 881 |
+
"▁حاض",
|
| 882 |
+
"چی",
|
| 883 |
+
"ادرش",
|
| 884 |
+
"▁سرو",
|
| 885 |
+
"▁آل",
|
| 886 |
+
"▁قل",
|
| 887 |
+
"▁وای",
|
| 888 |
+
"کاری",
|
| 889 |
+
"▁تول",
|
| 890 |
+
"▁نوع",
|
| 891 |
+
"یمه",
|
| 892 |
+
"بور",
|
| 893 |
+
"▁بف",
|
| 894 |
+
"ریکا",
|
| 895 |
+
"▁درآ",
|
| 896 |
+
"▁شور",
|
| 897 |
+
"ادش",
|
| 898 |
+
"اهای",
|
| 899 |
+
"▁سع",
|
| 900 |
+
"اموش",
|
| 901 |
+
"سا",
|
| 902 |
+
"▁چش",
|
| 903 |
+
"وبوس",
|
| 904 |
+
"گیری",
|
| 905 |
+
"▁مل",
|
| 906 |
+
"▁چک",
|
| 907 |
+
"شکرم",
|
| 908 |
+
"▁ضر",
|
| 909 |
+
"▁بان",
|
| 910 |
+
"▁بند",
|
| 911 |
+
"ذیر",
|
| 912 |
+
"خورد",
|
| 913 |
+
"▁شوم",
|
| 914 |
+
"▁پست",
|
| 915 |
+
"ژه",
|
| 916 |
+
"▁فرا",
|
| 917 |
+
"کو",
|
| 918 |
+
"ابت",
|
| 919 |
+
"گز",
|
| 920 |
+
"▁اقت",
|
| 921 |
+
"▁موش",
|
| 922 |
+
"جب",
|
| 923 |
+
"▁تاب",
|
| 924 |
+
"یه",
|
| 925 |
+
"ضای",
|
| 926 |
+
"ایهگ",
|
| 927 |
+
"▁بره",
|
| 928 |
+
"سل",
|
| 929 |
+
"اریخ",
|
| 930 |
+
"▁نیز",
|
| 931 |
+
"▁کلا",
|
| 932 |
+
"انیم",
|
| 933 |
+
"رمان",
|
| 934 |
+
"من",
|
| 935 |
+
"یند",
|
| 936 |
+
"یما",
|
| 937 |
+
"ردند",
|
| 938 |
+
"▁داخ",
|
| 939 |
+
"▁ببر",
|
| 940 |
+
"ئه",
|
| 941 |
+
"خصی",
|
| 942 |
+
"قا",
|
| 943 |
+
"▁محل",
|
| 944 |
+
"▁بقی",
|
| 945 |
+
"کش",
|
| 946 |
+
"▁سعی",
|
| 947 |
+
"▁پاک",
|
| 948 |
+
"تل",
|
| 949 |
+
"▁سرد",
|
| 950 |
+
"▁عر",
|
| 951 |
+
"▁هد",
|
| 952 |
+
"بته",
|
| 953 |
+
"▁راب",
|
| 954 |
+
"▁متو",
|
| 955 |
+
"▁تک",
|
| 956 |
+
"▁بیش",
|
| 957 |
+
"▁ره",
|
| 958 |
+
"عث",
|
| 959 |
+
"مئ",
|
| 960 |
+
"تمال",
|
| 961 |
+
"تونی",
|
| 962 |
+
"▁گشت",
|
| 963 |
+
"ادت",
|
| 964 |
+
"▁دهد",
|
| 965 |
+
"▁صبر",
|
| 966 |
+
"ران",
|
| 967 |
+
"▁جز",
|
| 968 |
+
"انس",
|
| 969 |
+
"▁تج",
|
| 970 |
+
"▁برق",
|
| 971 |
+
"جو",
|
| 972 |
+
"اریم",
|
| 973 |
+
"▁پن",
|
| 974 |
+
"▁قول",
|
| 975 |
+
"صل",
|
| 976 |
+
"▁برا",
|
| 977 |
+
"رمز",
|
| 978 |
+
"گرد",
|
| 979 |
+
"احت",
|
| 980 |
+
"▁وض",
|
| 981 |
+
"میت",
|
| 982 |
+
"▁رد",
|
| 983 |
+
"کنون",
|
| 984 |
+
"▁خیر",
|
| 985 |
+
"▁وال",
|
| 986 |
+
"▁چپ",
|
| 987 |
+
"▁مغ",
|
| 988 |
+
"ریف",
|
| 989 |
+
"بیل",
|
| 990 |
+
"قل",
|
| 991 |
+
"انهی",
|
| 992 |
+
"قد",
|
| 993 |
+
"▁",
|
| 994 |
+
"ا",
|
| 995 |
+
"ی",
|
| 996 |
+
"ر",
|
| 997 |
+
"م",
|
| 998 |
+
"ن",
|
| 999 |
+
"د",
|
| 1000 |
+
"ه",
|
| 1001 |
+
"و",
|
| 1002 |
+
"ت",
|
| 1003 |
+
"ب",
|
| 1004 |
+
"ک",
|
| 1005 |
+
"س",
|
| 1006 |
+
"ش",
|
| 1007 |
+
"ل",
|
| 1008 |
+
"ز",
|
| 1009 |
+
"خ",
|
| 1010 |
+
"ف",
|
| 1011 |
+
"گ",
|
| 1012 |
+
"ق",
|
| 1013 |
+
"ج",
|
| 1014 |
+
"ع",
|
| 1015 |
+
"چ",
|
| 1016 |
+
"پ",
|
| 1017 |
+
"ح",
|
| 1018 |
+
"آ",
|
| 1019 |
+
"ط",
|
| 1020 |
+
"ص",
|
| 1021 |
+
"غ",
|
| 1022 |
+
"ذ",
|
| 1023 |
+
"ض",
|
| 1024 |
+
"ظ",
|
| 1025 |
+
"ث",
|
| 1026 |
+
"ئ",
|
| 1027 |
+
"ژ",
|
| 1028 |
+
"ؤ",
|
| 1029 |
+
"<blank>"
|
| 1030 |
+
]
|
| 1031 |
+
}
|