File size: 5,144 Bytes
23590ba edcef16 8b6f23d d114f5b 4164780 d114f5b 4164780 d114f5b c80867b 07478ab d114f5b 250b797 d114f5b 250b797 d114f5b 4164780 d114f5b b90ae45 d114f5b edcef16 d114f5b b90ae45 d114f5b b90ae45 d114f5b 8b6f23d d114f5b 4164780 d114f5b 4164780 d114f5b edcef16 981b713 9225a67 8b6f23d |
1 2 3 4 5 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 |
import json
import tempfile
import ffmpeg
import os
import wave
from flask import Flask, request, jsonify
from flask_cors import CORS
from vosk import Model, KaldiRecognizer
from flasgger import Swagger
# Thư mục chứa model
MODEL_PATH = "model/vosk-model"
print("\u2705 Đang tải model Vosk...")
model = Model(MODEL_PATH)
# Khởi tạo Flask app
app = Flask(__name__)
CORS(app)
Swagger(app)
@app.route("/")
def home():
"""API Home
---
responses:
200:
description: API đang chạy
"""
return "\u2705 Vosk STT API đang chạy!"
@app.route("/stt", methods=["POST"])
def stt():
"""Chuyển đổi giọng nói thành văn bản (Speech-to-Text)
---
consumes:
- multipart/form-data
parameters:
- in: formData
name: file
type: file
required: true
description: File âm thanh WebM (sẽ được chuyển đổi sang WAV mono PCM)
responses:
200:
description: Kết quả chuyển đổi văn bản
schema:
type: object
properties:
text:
type: string
example: "Xin chào thế giới"
400:
description: Lỗi nếu file âm thanh không hợp lệ hoặc không tìm thấy
500:
description: Lỗi server nội bộ
"""
if "file" not in request.files:
return jsonify({"error": "Không tìm thấy file âm thanh! Vui lòng gửi trường 'file'."}), 400
audio_file = request.files["file"]
if audio_file.filename == "":
return jsonify({"error": "Không có file được chọn!"}), 400
# Lưu file WebM tạm thời
with tempfile.NamedTemporaryFile(suffix=".webm", delete=False) as temp_webm_file:
webm_path = temp_webm_file.name
audio_file.save(webm_path)
# Kiểm tra kích thước tệp
if os.path.getsize(webm_path) < 100:
os.remove(webm_path)
return jsonify({"error": "Tệp âm thanh quá nhỏ hoặc rỗng!"}), 400
# Đường dẫn file WAV tạm thời sau khi chuyển đổi
wav_path = None
wf = None
try:
# Kiểm tra tệp WebM bằng ffprobe trước khi chuyển đổi
try:
probe = ffmpeg.probe(webm_path)
if 'streams' not in probe or not any(s['codec_type'] == 'audio' for s in probe['streams']):
raise ValueError("Tệp không chứa luồng âm thanh hợp lệ!")
except ffmpeg.Error as e:
error_message = e.stderr.decode('utf-8') if e.stderr else str(e)
return jsonify({"error": f"Lỗi kiểm tra tệp WebM: {error_message}"}), 500
# Chuyển đổi WebM sang WAV mono PCM
wav_path = tempfile.mktemp(suffix=".wav")
stream = ffmpeg.input(webm_path)
stream = ffmpeg.output(
stream,
wav_path,
acodec="pcm_s16le", # PCM 16-bit signed little-endian
ac=1, # Mono
ar=16000, # Tần số mẫu 16kHz, phù hợp với Vosk
format="wav"
)
# Thêm cờ -vn để bỏ qua video nếu có và -y để ghi đè
ffmpeg.run(stream, overwrite_output=True, quiet=True, capture_stdout=True, capture_stderr=True)
# Mở file WAV đã chuyển đổi
wf = wave.open(wav_path, "rb")
# Kiểm tra định dạng WAV mono PCM
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getcomptype() != "NONE":
return jsonify({"error": "Định dạng WAV sau chuyển đổi không đúng!"}), 400
# Khởi tạo KaldiRecognizer
rec = KaldiRecognizer(model, wf.getframerate())
result_text = ""
# Đọc và xử lý dữ liệu âm thanh
while True:
data = wf.readframes(4000)
if len(data) == 0:
break
if rec.AcceptWaveform(data):
result = json.loads(rec.Result())
result_text += result.get("text", "") + " "
else:
partial_result = json.loads(rec.PartialResult())
if partial_result.get("partial", ""):
result_text += partial_result["partial"] + " "
# Trả về kết quả đã xử lý
final_text = result_text.strip()
if not final_text:
final_text = "Không nhận diện được nội dung âm thanh."
return jsonify({"text": final_text})
except ffmpeg.Error as e:
error_message = e.stderr.decode('utf-8') if e.stderr else str(e)
return jsonify({"error": f"Lỗi chuyển đổi âm thanh từ WebM sang WAV: {error_message}"}), 500
except Exception as e:
return jsonify({"error": f"Lỗi xử lý âm thanh: {str(e)}"}), 500
finally:
# Đóng file WAV nếu đã mở
if wf is not None:
wf.close()
# Xóa các file tạm
for path in [webm_path, wav_path]:
if path and os.path.exists(path):
os.remove(path)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860, debug=True) |