自建 Whisper API 作为安卓语音输入键盘
自建、开源、无需切换即可支持多国语言的安卓语音输入
阅读时间 2 分钟
Whisper 是 OpenAI 开发的预训练语音转文本模型。使用的训练数据量非常大,特点是支持多种语言并且鲁棒性强,可以识别演讲、歌曲。能够进行人声检测、语言识别、语音转录、甚至是语音翻译。
使用 Whisper 的方式有几种。OpenAI 开源了模型的权重,因此可以把模型下载到本地运行。同时 OpenAI 也提供了在线 API 服务。最大的模型 large-v2
需要 4G 显存即可推理,正好我这有块吃灰显卡能跑。
后端决定了自建,那么输入法前端呢?我在 Github 上仔细找了大半天,都没找到能通过调用 OpenAI Whisper 进行语音输入的安卓键盘。以下是我找到的其他相关项目
OpenAI Whisper Keyboard - Google Play 使用运行在手机本地的
small
模型,只支持英文,并且严格来说这不是输入法程序,而是记事本程序Kõnele 使用 kaldi 后端进行语音识别的安卓输入法
Konele 支持自定义语音识别 URL, 虽然使用的不是 OpenAI API,但可以偷梁换柱自己用 FastAPI 写个接口转换 API 格式。
以下是我使用的 FastAPI 代码
import wave
import hashlib
import argparse
from datetime import datetime
import os
from typing import Any
from fastapi import File, UploadFile, Form, FastAPI, Request
from src.whisper_ctranslate2.whisper_ctranslate2 import TranscriptionOptions
from src.whisper_ctranslate2.writers import format_timestamp
import opencc
ccc = opencc.OpenCC("t2s.json")
transcriber = Transcribe(
model_path="large-v2",
device="auto",
device_index=0,
compute_type="default",
threads=1,
cache_directory="",
local_files_only=False,
)
@app.post("/android")
async def translateapi(request: Request, task: str = "transcribe"):
content_type = request.headers.get("Content-Type", "")
print("task", task)
print("downloading request file", content_type)
splited = [i.strip() for i in content_type.split(",") if "=" in i]
info = {k: v for k, v in (i.split("=") for i in splited)}
print(info)
channels = int(info.get("channels", "1"))
rate = int(info.get("rate", "16000"))
body = await request.body()
md5 = hashlib.md5(body).hexdigest()
filename = datetime.now().strftime("%Y%m%d-%H%M%S") + "." + md5 + ".wav"
# save the file to a temporary location
file_path = os.path.join("./cache", filename)
with wave.open(file_path, "wb") as buffer:
buffer.setnchannels(channels)
buffer.setsampwidth(2)
buffer.setframerate(rate)
buffer.writeframes(body)
options = TranscriptionOptions(
beam_size=5,
best_of=5,
patience=1.0,
length_penalty=1.0,
log_prob_threshold=-1.0,
no_speech_threshold=0.6,
compression_ratio_threshold=2.4,
condition_on_previous_text=True,
temperature=[0.0, 1.0 + 1e-6, 0.2],
suppress_tokens=[-1],
word_timestamps=True,
print_colors=False,
prepend_punctuations="\"'“¿([{-",
append_punctuations="\"'.。,,!!??::”)]}、",
vad_filter=False,
vad_threshold=None,
vad_min_speech_duration_ms=None,
vad_max_speech_duration_s=None,
vad_min_silence_duration_ms=None,
initial_prompt=initial_prompt,
)
result = transcriber.inference(
audio=file_path,
task=task,
language="",
verbose=False,
live=False,
options=options,
)
text = result.get("text", "")
text = ccc.convert(text)
print("result", text)
return {
"status": 0,
"hypotheses": [{"utterance": text}],
"id": md5,
}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--host", default="0.0.0.0", type=str)
parser.add_argument("--port", default=5000, type=int)
args = parser.parse_args()
import uvicorn
uvicorn.run(app, host=args.host, port=args.port)
其中的 OpenCC
库是用来繁体转简体中文。这是因为 Whisper 在训练时并没有区分简繁体,因此输出有可能是简体或繁体。
后端服务启动后,在 konele 中设置语音识别服务为 Kõnele (grammar support)
,将该服务的 Server URL 设置为 http://<your-server>/android
即可
若要在语音识别的同时进行翻译(到英文),可以将 Server URL 设为 http://<your-server>/android?task=translate
,实现任意语音输入 -> 英语输出
温馨提示: Konele 有个 bug,更改 Server URL 后需要到 App 详情页面强制停止 App 后,设置才能生效