掘金 后端 ( ) • 2024-05-08 16:35

theme: devui-blue

如何在本地搭建一个中文语音服务

为什么不在服务器搭建?为什么?因为我穷,买不起性能好的服务器,8G的内存都不够用跑不起来的感觉,16G太贵了

安装Python环境

安装依赖包

依次运行下面的命令安装对应的包

pip3 intall funasr
pip3 install modelscope
pip3 install torch torchaudio

funasr 阿里开源的一个大模型
modelscope魔搭社区 一个由阿里达摩院开发的模型服务平台
torchtorchaudio 是两个不同的Python库,它们通常一起使用以支持音频处理任务。

编写python代码

首先先创建voice.py文件, 然后指定模型目录,否则funasr会在C盘下载模型,导致C盘会很大,运行的时候这些模型首次会自己下载,会比较慢。

from modelscope.pipelines import pipeline  
from modelscope.utils.constant import Tasks  

from modelscope.hub.snapshot_download import snapshot_download
  
# 指定本地目录
local_dir_root = "./models_from_modelscope"
# 使用modelscope的模型库,里面有很多模型,可以自己试着换一下,这里使用的是长文本离线带字符串的模型
model_dir = snapshot_download('damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch', cache_dir=local_dir_root)
  
inference_pipeline = pipeline(  
    task=Tasks.auto_speech_recognition,  
    model=model_dir,  
    vad_model='damo/speech_fsmn_vad_zh-cn-16k-common-pytorch',
    punc_model='damo/punc_ct-transformer_zh-cn-common-vocab272727-pytorch',
    #lm_model='damo/speech_transformer_lm_zh-cn-common-vocab8404-pytorch',  
    #lm_weight=0.15,  
    #beam_size=10,  
)  
param_dict = {}  
param_dict['use_timestamp'] = False
def transcribe_one(audio_path):  
    rec_result = inference_pipeline(input=audio_path, param_dict=param_dict)
    return rec_result

audio_path = "example_zh.wav" # 本地wav音频文件
transcribed_text = transcribe_one(audio_path)
print(transcribed_text) # 打印转录后的文本

命令行运行python脚本

python voice.py

如果遇到报错,大多数是包版本不兼容

当时我运行的时候也报错包版本不兼容,我使用的是

torchaudio 2.2.2
torch 2.2.2

然后不出意外的话控制台就会输出example_zh.wav音频中的文字

搭建一个web服务器端调用返回文字识别结果

  1. 新建一个 serve.py 文件
  2. 安装以下包
pip install flask
pip install flask_cors
  1. 使用 flaskserve.py搭建一个web服务器
from flask import Flask, request, jsonify
import os
from flask_cors import CORS

# 引入voice.py的语音识别函数
from voice import transcribe_one

app = Flask(__name__)
# 允许跨域
cors = CORS(app, resources={r'/*': {'origins': '*'}})

@app.route('/transcribe', methods=['POST'])

def transcribe_audio():
  return jsonify({'transcription': '请求进来了'})


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
  1. 运行serve.py,访问http://localhost:5000/transcribe 能看到内容即可
  2. 首先下载ffmpeg,解压到某个盘,在把解压的路径添加到系统环境变量path下如(E:\ffmpeg\bin),cmd运行ffmpeg看是否成功
  3. 新建一个文件夹 tmp 用来存放临时WebM文件
  4. ffmpeg把前端传来的格式转为 wav 格式
from flask import Flask, request, jsonify
import os
from flask_cors import CORS
import ffmpeg

# 引入您的语音识别函数和相关库
from voice import transcribe_one

app = Flask(__name__)
cors = CORS(app, resources={r'/*': {'origins': '*'}})

@app.route('/transcribe', methods=['POST'])

def transcribe_audio():
    # 获取前端上传的音频文件,前端传过来的名字叫recording
    audio_file = request.files.get('recording')
    # 检查文件是否上传成功
    if not audio_file:
        return jsonify({'error': 'No audio file uploaded.'}), 400

    # 保存临时WebM文件
    temp_webm_path = os.path.join('tmp', audio_file.filename)  # 临时WebM文件路径
    audio_file.save(temp_webm_path)
    # 使用ffmpeg库进行WebM转WAV
    
    try:
        (
            ffmpeg
            .input(temp_webm_path)
            .output(os.path.splitext(temp_webm_path)[0] + '.wav', acodec='pcm_s16le', ar='16000', ac='1')
            .overwrite_output()
            .run()
        )
    except ffmpeg.Error as e:
        return jsonify({'error': str(e)}), 500
    # 使用转换后的WAV文件进行语音识别
    try:
        transcribed_text = transcribe_one(os.path.splitext(temp_webm_path)[0] + '.wav')
    except Exception as e:
        return jsonify({'error': str(e)}), 500

    # 删除临时文件(可选,根据需要决定是否保留)
    # os.remove(temp_webm_path)
    # os.remove(os.path.splitext(temp_webm_path)[0] + '.wav')
    # 返回转录结果
    print(transcribed_text)
    return jsonify({'transcription': transcribed_text})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

  1. 启动python server.py
python server.py

前端实现

  1. 新建一个 index.html 文件,并添加以下内容,请求刚刚启动的服务
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      #btn {
        width: 100px;
        height: 50px;
        background-color: #4caf50;
        color: white;
        border: none;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div>
      <button id="btn">按住录音</button>
      <div id="result"></div>
    </div>
  </body>

  <script>
    const btn = document.getElementById("btn");

    btn.addEventListener("touchstart", startRecording);
    btn.addEventListener("touchend", stopRecording);
    let isRecording = false;
    let mediaRecorder = null;
    let chunks = [];

    function startRecording() {
      if (isRecording) return; // 防止重复启动

      navigator.mediaDevices
        .getUserMedia({ audio: true })
        .then((stream) => {
          mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });

          mediaRecorder.addEventListener("dataavailable", (event) => {
            if (event.data.size > 0) {
              chunks.push(event.data);
            }
          });

          mediaRecorder.start();

          isRecording = true;
        })
        .catch((error) => {
          console.error("无法访问麦克风:", error);
        });
    }

    function stopRecording() {
      console.log(222);
      if (!isRecording) return; // 防止在未录制状态下停止

      isRecording = false;

      mediaRecorder.stop();

      mediaRecorder.addEventListener("stop", () => {
        const blob = new Blob(chunks, { type: "audio/webm" });
        sendRecordingToBackend(blob);
        chunks = []; // 清空缓存的chunks
      });
    }

    function sendRecordingToBackend(blob) {
      const formData = new FormData();
      formData.append("recording", blob, "recording.webm")
      fetch("http://localhost:5000/transcribe", {
        method: "POST",
        body: formData,
      }).then(response => response.json())
        .then((data) => {
          if(data?.transcription) {
            document.getElementById("result").innerHTML = data?.transcription[0].text
          }
      })
    }
  </script>
</html>
  1. 访问这个index.html页面,按住按钮,然后开始语音说话,文字就可以返回正确的结果了。

  2. 如果在运行python脚本的时候cmd关掉进程也不关掉的方法是使用pythonw serve.py这样就行了、还有就是把他变成一个window服务的方法。

  3. 以上就是语音转文字的实现方法了。