Modification of the STT structure
This commit is contained in:
+26
-15
@@ -177,27 +177,38 @@ def _wav_to_float32(wav_bytes: bytes) -> tuple[np.ndarray, int]:
|
||||
return audio, rate
|
||||
|
||||
|
||||
def _resample_audio(audio: np.ndarray, src_rate: int, dst_rate: int = 16000) -> np.ndarray:
|
||||
if src_rate == dst_rate or audio.size == 0:
|
||||
return audio.astype(np.float32, copy=False)
|
||||
duration = audio.shape[0] / float(src_rate)
|
||||
new_len = max(1, int(round(duration * dst_rate)))
|
||||
x_old = np.linspace(0.0, 1.0, num=audio.shape[0], endpoint=False)
|
||||
x_new = np.linspace(0.0, 1.0, num=new_len, endpoint=False)
|
||||
return np.interp(x_new, x_old, audio).astype(np.float32)
|
||||
|
||||
|
||||
def _ensure_16k(wav_bytes: bytes) -> np.ndarray:
|
||||
audio, rate = _wav_to_float32(wav_bytes)
|
||||
if rate == 16000:
|
||||
return audio
|
||||
|
||||
# Prefer ffmpeg when available; otherwise pure-numpy resample (browser often sends 44.1/48 kHz)
|
||||
ffmpeg = shutil.which("ffmpeg")
|
||||
if not ffmpeg:
|
||||
raise HTTPException(status_code=400, detail="نرخ نمونهبرداری صوت پشتیبانی نشد")
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src = Path(tmp) / "in.wav"
|
||||
dst = Path(tmp) / "out.wav"
|
||||
src.write_bytes(wav_bytes)
|
||||
result = subprocess.run(
|
||||
[ffmpeg, "-y", "-i", str(src), "-ac", "1", "-ar", "16000", "-f", "wav", str(dst)],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0 or not dst.exists():
|
||||
raise HTTPException(status_code=400, detail="نرمالسازی صوت ناموفق بود")
|
||||
audio, _ = _wav_to_float32(dst.read_bytes())
|
||||
return audio
|
||||
if ffmpeg:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src = Path(tmp) / "in.wav"
|
||||
dst = Path(tmp) / "out.wav"
|
||||
src.write_bytes(wav_bytes)
|
||||
result = subprocess.run(
|
||||
[ffmpeg, "-y", "-i", str(src), "-ac", "1", "-ar", "16000", "-f", "wav", str(dst)],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode == 0 and dst.exists():
|
||||
audio, _ = _wav_to_float32(dst.read_bytes())
|
||||
return audio
|
||||
|
||||
return _resample_audio(audio, rate, 16000)
|
||||
|
||||
|
||||
def _transcribe_local(wav_bytes: bytes) -> str:
|
||||
|
||||
@@ -137,6 +137,25 @@ function mergeChunks(parts: Float32Array[]) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Downsample to 16 kHz so STT does not depend on server ffmpeg. */
|
||||
function resampleTo16k(samples: Float32Array, srcRate: number) {
|
||||
const dstRate = 16000;
|
||||
if (!srcRate || srcRate === dstRate) return { samples, rate: srcRate || dstRate };
|
||||
const ratio = srcRate / dstRate;
|
||||
const newLen = Math.max(1, Math.floor(samples.length / ratio));
|
||||
const out = new Float32Array(newLen);
|
||||
for (let i = 0; i < newLen; i += 1) {
|
||||
const srcIndex = i * ratio;
|
||||
const i0 = Math.floor(srcIndex);
|
||||
const i1 = Math.min(samples.length - 1, i0 + 1);
|
||||
const t = srcIndex - i0;
|
||||
const a = samples[i0] ?? 0;
|
||||
const b = samples[i1] ?? a;
|
||||
out[i] = a + (b - a) * t;
|
||||
}
|
||||
return { samples: out, rate: dstRate };
|
||||
}
|
||||
|
||||
function encodeWav(samples: Float32Array, rate: number) {
|
||||
const bytes = samples.length * 2;
|
||||
const buffer = new ArrayBuffer(44 + bytes);
|
||||
@@ -189,7 +208,8 @@ async function finish(keep: boolean) {
|
||||
|
||||
setTranscribing(true);
|
||||
try {
|
||||
const text = await upload(encodeWav(samples, rate));
|
||||
const prepared = resampleTo16k(samples, rate);
|
||||
const text = await upload(encodeWav(prepared.samples, prepared.rate));
|
||||
if (!text) {
|
||||
setError('متنی از صدا تشخیص داده نشد.');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user