【超简单】之基于PaddleSpeech语音听写桌面应用

1,089 阅读5分钟

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

一、【超简单】之基于PaddleSpeech语音听写桌面应用

此项目前情回顾:【超简单】之基于PaddleSpeech搭建个人语音听写服务 aistudio.baidu.com/aistudio/pr…

此次主要做了以下工作:

通过 QGUI 实现语音听写服务桌面化,方便非程序员使用。

  • 1.撰写qgui界面
  • 2.结果可视化
  • 3.保存听写结果到txt文件

二、环境搭建

1.PaddleSpeech简介

PaddleSpeech 是基于飞桨 PaddlePaddle 的语音方向的开源模型库,用于语音和音频中的各种关键任务的开发,包含大量基于深度学习前沿和有影响力的模型,一些典型的应用如下:

  • 语音识别
  • 语音翻译
  • 语音合成

2.PaddleSpeech安装

pip install paddlespeech

2.1 相关依赖

  • gcc >= 4.8.5
  • paddlepaddle >= 2.3.1
  • python >= 3.7
  • linux(推荐), mac, windows

2.2 win安装注意事项

3.QGUI简介

Downloads GitHub forks GitHub Repo stars GitHub
QGUI - 低于100k的超轻量桌面图形化框架,可通过几行代码、使用模板来快捷制作出属于你的图形化界面

4.QGUI安装

  • 通用方式 python -m pip install qgui

  • 国内推荐 python -m pip install qgui -i https://mirrors.bfsu.edu.cn/pypi/web/simple

  • 运行Demo/安装测试 python -m qgui

三、应用设计

1.界面设计

主要是wav文件读取,保存文件设置

from qgui import CreateQGUI
from qgui.banner_tools import BaseBarTool, GitHub
from qgui.notebook_tools import ChooseFileTextButton, RunButton, ChooseDirTextButton
import listen
import warnings

warnings.filterwarnings('ignore')

def click(args):
    print("你点到我啦~")
    print("输入框文字为:", args["文件选择"].get())
    print("输入框文字为:", args["保存位置"].get())
    listen.run(args["文件选择"].get(), args["保存位置"].get())


# 创建主界面
main_gui = CreateQGUI(title="PaddleSpeech语音自助听写")
# 在主界面部分添加一个文件选择工具吧~
# 选择录音文件
main_gui.add_notebook_tool(ChooseFileTextButton(name="文件选择", filetypes="wav" ))
# 在主界面添加保存位置
# 选择保存目录
main_gui.add_notebook_tool(ChooseDirTextButton(name="保存位置"))
# 要不要再添加一个运行按钮?,绑定刚刚创建的函数吧~
main_gui.add_notebook_tool(RunButton(click))
# 简单加个简介
main_gui.set_navigation_about(author="JavaRoom",
                              version="0.0.1",
                              github_url="https://aistudio.baidu.com/aistudio/personalcenter/thirdview/89263",
                              other_info=["PaddleSpeech语音自助听写"])
# 跑起来~
main_gui.run()

2.听写服务函数化

主要函数如下:

  • qiefen(path, ty='audio', mmin_dur=1, mmax_dur=100000, mmax_silence=1, menergy_threshold=55) 实现长语音切分
  • audio2txt(path) 实现语音转文本
  • txt2csv(txt_all) 文本保存
  • correct_punctuation(source_path='听写录音.csv') 标点符号转写
  • run(wav_file_path, target_dir) 根据给定的文本,完成听写并保存
from paddlespeech.cli.asr.infer import ASRExecutor
import csv
import moviepy.editor as mp
import auditok
import os
import paddle
from paddlespeech.cli import ASRExecutor, TextExecutor
import soundfile
import librosa
import warnings
# 引入auditok库
import auditok
import csv
import datetime
import shutil


warnings.filterwarnings('ignore')



# 输入类别为audio
def qiefen(path, ty='audio', mmin_dur=1, mmax_dur=100000, mmax_silence=1, menergy_threshold=55):
    audio_file = path
    audio, audio_sample_rate = soundfile.read(
        audio_file, dtype="int16", always_2d=True)

    audio_regions = auditok.split(
        audio_file,
        min_dur=mmin_dur,  # minimum duration of a valid audio event in seconds
        max_dur=mmax_dur,  # maximum duration of an event
        # maximum duration of tolerated continuous silence within an event
        max_silence=mmax_silence,
        energy_threshold=menergy_threshold  # threshold of detection
    )

    for i, r in enumerate(audio_regions):
        # Regions returned by `split` have 'start' and 'end' metadata fields
        print(
            "Region {i}: {r.meta.start:.3f}s -- {r.meta.end:.3f}s".format(i=i, r=r))

        epath = ''
        file_pre = str(epath.join(audio_file.split('.')[0].split('/')[-1]))

        mk = 'change'
        if (os.path.exists(mk) == False):
            os.mkdir(mk)
        if (os.path.exists(mk + '/' + ty) == False):
            os.mkdir(mk + '/' + ty)
        if (os.path.exists(mk + '/' + ty + '/' + file_pre) == False):
            os.mkdir(mk + '/' + ty + '/' + file_pre)
        num = i
        # 为了取前三位数字排序
        s = '000000' + str(num)

        file_save = mk + '/' + ty + '/' + file_pre + '/' + \
                    s[-3:] + '-' + '{meta.start:.3f}-{meta.end:.3f}' + '.wav'
        filename = r.save(file_save)
        print("region saved as: {}".format(filename))
    return mk + '/' + ty + '/' + file_pre

# 语音转文本
asr_executor = ASRExecutor()

# 听写
def audio2txt(path):
    # 返回path下所有文件构成的一个list列表
    print(f"path: {path}")
    filelist = os.listdir(path)
    # 保证读取按照文件的顺序
    filelist.sort(key=lambda x: int(os.path.splitext(x)[0][:3]))
    # 遍历输出每一个文件的名字和类型
    words = []
    for file in filelist:
        print(path + '/' + file)
        text = asr_executor(
            audio_file=path + '/' + file,
            device=paddle.get_device(), force_yes=True) # force_yes参数需要注意
        words.append(text)
    return words



# 保存
def txt2csv(txt_all):
    with open(f'听写录音.csv', 'w', encoding='utf-8') as f:
        f_csv = csv.writer(f)
        for row in txt_all:
            f_csv.writerow([row])

# 纠正标点符号
def correct_punctuation(source_path='听写录音.csv'):
    # 拿到新生成的音频的路径
    texts = ''
    with open(source_path, 'r') as f:
        text = f.readlines()
    for i in range(len(text)):
        text[i] = text[i].replace('\n', '')
        texts = texts + text[i]
    # print(texts)
    text_executor = TextExecutor()
    if text:
        result = text_executor(
            text=texts,
            task='punc',
            model='ernie_linear_p3_wudao',
            device=paddle.get_device(),
            # force_yes=True
        )
    # print(result)
    return result


def run(wav_file_path, target_dir):
    # 可替换成自身的录音文件
    # wav_file_path = '录音.wav'
    # 划分音频
    path = qiefen(path=wav_file_path, ty='audio',
                    mmin_dur=0.5, mmax_dur=100000, mmax_silence=0.5, menergy_threshold=55)
    # 音频转文本  需要GPU
    txt_all = audio2txt(path)
    # 存入csv
    txt2csv(txt_all)
    # 纠正标点
    now_date=str(datetime.datetime.now())
    source_path = f'听写转录音_{now_date[:10]}_{txt_all[0]}.txt'
    correct_txt=correct_punctuation()
    print(correct_txt)
    print(len(correct_txt))
    with open(source_path, 'w') as f:
        f.writelines(correct_txt)
    # 删除中间过程文件  
    os.remove("听写录音.csv")
    shutil.rmtree('exp')

四、最终形态

最终形态为一个带节目应用程序,通过给定的音频,自动听写,并保存至文本文件。

五、QGUI个性修改

1.按扩展名选择文件

修改 qgui/nobook_tools.py 第136行 内容

        # 加扩展名判断,只加了wav
        if filetypes == 'wav':
            self.filetypes = [('WAV Files', 'wav')] 
        else:
            self.filetypes = [('All Files', '*')] 

调用时加 filetypes 扩展选项

# 选择录音文件
main_gui.add_notebook_tool(ChooseFileTextButton(name="文件选择", filetypes="wav" ))

2.修改界面github地址

修改qgui/base_frame.py/BaseNavigation 第80行 内容为aistudio

class BaseNavigation(_Backbone):
    """
    左侧导航栏基本框架
    """

    def __init__(self, style="primary"):
        super(BaseNavigation, self).__init__(f_style=style)
        self.tabs = dict()

    def add_about(self,
                  author: str = "未知作者",
                  version: str = "0.0.1",
                  github_url: str = None,
                  other_info: List[str] = None):
        bus_cf = CollapsingFrame(self.frame)
        bus_cf.pack(fill='x', pady=0)

        bus_frm = ttk.Frame(bus_cf, padding=5)
        bus_frm.columnconfigure(1, weight=1)
        bus_cf.add(bus_frm, title="相关信息", style='secondary.TButton')

        ttk.Label(bus_frm, text=f"作者:\t{author}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw")
        ttk.Label(bus_frm, text=f"版本:\t{version}", style="TLabel", justify="left", wraplength=160).pack(anchor="nw")

        if other_info:
            for line in other_info:
                ttk.Label(bus_frm, text=line, style="TLabel").pack(anchor="nw")

        if github_url:
            def github_callback(event):
                webbrowser.open_new(github_url)

            github_label = ttk.Label(bus_frm, text=f"> 进入Aistudio", style="info.TLabel", justify="left")
            github_label.pack(anchor="nw")
            github_label.bind("<Button-1>", github_callback)

六、后续计划

  • 1.添加音频转码功能
  • 2.添加视频语音提取功能
  • 3.添加离线部署方案
  • 4.进行打包