241205_给自己的应用加上语音助手功能

241205_给自己的应用加上语音助手功能

前面我们自己做了一个网易云音乐,但每次都要去点点点显得有点麻烦,所以我就考虑添加一些语音助手的功能。

其实当前在日常windows使用中,我觉得也就音乐播放需要一个语音助手交互,其他的功能,要么太复杂,简单的声控无法实现,要么语音控制的效率太低,不如直接上手操作。

最简单的语音助手仅需要实现三个功能,本地语音转文本,根据文本内容提取关键词完成对应指令,播放“收到”“已完成”等语音,我们此处就实现这样的最简单的语音助手。

语音转文字

本地语音转文字我们使用vosk语音识别库实现。model使用本地的一个60m的小模型.

这里附模型链接: https://pan.baidu.com/s/1mY2VoRqjSwS6n4qj1pPZMA?pwd=8888

解压放在项目根目录就可以了

为了实现一个较好的唤醒效果,我们采用后台实时录音的方式。检测到声音的时候就进行转文字,但是因为环境噪音是一直存在的,此时我们就要给他设定一个阈值,当超过这个阈值时,我们才认为他是有效输入,才进行转文字输入。

def SaveWave(model):
    # 设置音频参数
    FORMAT = pyaudio.paInt16    # 音频流的格式
    RATE = 44100                # 采样率,单位Hz
    CHUNK = 4000                # 单位帧
    THRESHOLDNUM = 10           # 静默时间,超过这个个数就保存文件
    THRESHOLD = 100             # 设定停止采集阈值

    audio = pyaudio.PyAudio()
    stream = audio.open(format=FORMAT,
                        channels=1,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)
    frames = []
    print("开始录音...")
    count = 0
    while count < THRESHOLDNUM:
        data = stream.read(CHUNK, exception_on_overflow=False)
        np_data = np.frombuffer(data, dtype=np.int16)
        frame_energy = np.mean(np.abs(np_data))
        # print(frame_energy)
        # 如果能量低于阈值持续时间过长,则停止录音
        if frame_energy < THRESHOLD:
            count += 1
        elif count > 0:
            count -= 1

        frames.append(data)
    print("停止录音!")
    stream.stop_stream()
    stream.close()
    audio.terminate()

    rec = KaldiRecognizer(model, RATE)
    rec.SetWords(True)
    str_ret = ""
    for data in frames:
        if rec.AcceptWaveform(data):
            result = json.loads(rec.Result())
            if 'text' in result:
                str_ret += result['text']

    result = json.loads(rec.FinalResult())
    if 'text' in result:
        str_ret += result['text']

    str_ret = "".join(str_ret.split())
    return str_ret

执行命令

得到语音转文字的结果之后,我们就可指定对应执行的指令了,因为我们不需要联网去搜索结果,只需要定义我们自己能用到的指令就可以了,此处简单定义指令。

def weekup(mw):
    model = Model("vosk-model-cn-0.15")
    SetLogLevel(-1)

    while 1:
        res = SaveWave(model)
        if res != "" and res != None:
            print(res)
        if "小爱同学" in res:
            answer = "我在"
            answermethod(answer)

        if "聊天" in res:
            answer = "好的,请开始"
            answermethod(answer)
            from stt import run as stt_run
            result=stt_run()
        if "打开" in res:
            app_name=res.replace("打开",'').strip()
            if app_name in dir_path:
                app_path=dir_path[app_name]
                import subprocess
                subprocess.Popen(app_path)
                answer = "好的,已经为您打开了"
                answermethod(answer)
        if "歌" in res:
            from testyuncloud import yuncloudMW
            mw.playMusic()
            # print("播放歌曲")
        if "暂停" in res:
            mw.playMusic()
        if "上一首" in res:
            mw.prevMusic()
        if "下一首" in res:
            mw.nextMusic()

在这个方法中,我调用了一个answermethod方法,在这个方法中,我调用了百度的语音合成api,当然这里合成的都是一些简单的语音,你也可以直接自己录音,然后直接播放录音文件。就避免了调用api。

下面是answermethod以及百度语音合成api的调用。

def answermethod(answer):
    baidu_tts_test(answer)
    time.sleep(1)
# coding=utf-8
import sys
import json
import pygame
import time
import io
IS_PY3 = sys.version_info.major == 3
if IS_PY3:
    from urllib.request import urlopen
    from urllib.request import Request
    from urllib.error import URLError
    from urllib.parse import urlencode
    from urllib.parse import quote_plus
else:
    import urllib2
    from urllib import quote_plus
    from urllib2 import urlopen
    from urllib2 import Request
    from urllib2 import URLError
    from urllib import urlencode

API_KEY = ' ' # 这里和下面替换成你的百度语音合成服务api的key
SECRET_KEY = ' '

# TEXT = "欢迎使用百度语音合成。"

# 发音人选择, 基础音库:0为度小美,1为度小宇,3为度逍遥,4为度丫丫,
# 精品音库:5为度小娇,103为度米朵,106为度博文,110为度小童,111为度小萌,默认为度小美 
PER = 4
# 语速,取值0-15,默认为5中语速
SPD = 5
# 音调,取值0-15,默认为5中语调
PIT = 5
# 音量,取值0-9,默认为5中音量
VOL = 5
# 下载的文件格式, 3:mp3(default) 4: pcm-16k 5: pcm-8k 6. wav
AUE = 3

FORMATS = {3: "mp3", 4: "pcm", 5: "pcm", 6: "wav"}
FORMAT = FORMATS[AUE]

CUID = "123456PYTHON"

TTS_URL = 'http://tsn.baidu.com/text2audio'


class DemoError(Exception):
    pass


"""  TOKEN start """

TOKEN_URL = 'http://aip.baidubce.com/oauth/2.0/token'
SCOPE = 'audio_tts_post'  # 有此scope表示有tts能力,没有请在网页里勾选


def fetch_token():
    print("fetch token begin")
    params = {'grant_type': 'client_credentials',
              'client_id': API_KEY,
              'client_secret': SECRET_KEY}
    post_data = urlencode(params)
    if (IS_PY3):
        post_data = post_data.encode('utf-8')
    req = Request(TOKEN_URL, post_data)
    try:
        f = urlopen(req, timeout=5)
        result_str = f.read()
    except URLError as err:
        print('token http response http code : ' + str(err.code))
        result_str = err.read()
    if (IS_PY3):
        result_str = result_str.decode()

    print(result_str)
    result = json.loads(result_str)
    print(result)
    if ('access_token' in result.keys() and 'scope' in result.keys()):
        if not SCOPE in result['scope'].split(' '):
            raise DemoError('scope is not correct')
        print('SUCCESS WITH TOKEN: %s ; EXPIRES IN SECONDS: %s' % (result['access_token'], result['expires_in']))
        return result['access_token']
    else:
        raise DemoError('MAYBE API_KEY or SECRET_KEY not correct: access_token or scope not found in token response')


"""  TOKEN end """

def baidu_tts_test(text,PER=0,SPD=10,PIT=PIT,VOL=VOL):
    token = fetch_token()
    tex = quote_plus(text)  # 此处TEXT需要两次urlencode
    print(tex)
    params = {'tok': token, 'tex': tex, 'per': PER, 'spd': SPD, 'pit': PIT, 'vol': VOL, 'aue': AUE, 'cuid': CUID,
              'lan': 'zh', 'ctp': 1}  # lan ctp 固定参数

    data = urlencode(params)
    print('test on Web Browser' + TTS_URL + '?' + data)

    req = Request(TTS_URL, data.encode('utf-8'))
    has_error = False
    try:
        f = urlopen(req)
        result_str = f.read()

        headers = dict((name.lower(), value) for name, value in f.headers.items())

        has_error = ('content-type' not in headers.keys() or headers['content-type'].find('audio/') < 0)
    except  URLError as err:
        print('asr http response http code : ' + str(err.code))
        result_str = err.read()
        has_error = True

    save_file = "error.txt" if has_error else 'result.' + FORMAT
    with open(save_file, 'wb') as of:
        of.write(result_str)

    if has_error:
        if (IS_PY3):
            result_str = str(result_str, 'utf-8')
        print("tts api  error:" + result_str)
    else:
        # 初始化pygame
        pygame.mixer.init()

        # 将音频数据加载到内存
        pygame.mixer.music.load(io.BytesIO(result_str))

        # 播放音频
        pygame.mixer.music.play()

        # # 防止程序立即退出
        # while pygame.mixer.music.get_busy():
        #     time.sleep(1)
    print("result saved as :" + save_file)

随后我要实现调整音量的功能,但是识别出来调整音量的百分比是汉字,无法直接去调整,此处就要建立汉字的数字到阿拉伯数字的映射。

from pypinyin import lazy_pinyin

def hanzi_to_number_pinyin(hanzi_str):
    hnd = {
        'ling': 0, 'yi': 1, 'er': 2, 'san': 3, 'si': 4, 'wu': 5, 'liu': 6, 'qi': 7, 'ba': 8, 'jiu': 9,
        'shi': 10, 'bai': 100, 'qian': 1000, 'wan': 10000, 'yi': 100000000
    }
    pinyin_list = lazy_pinyin(hanzi_str)
    result = 0
    temp = 0
    for pinyin in pinyin_list:
        if pinyin in hnd:
            digit = hnd[pinyin]
            if digit >= 10:
                if temp == 0:
                    temp = 1
                result += temp * digit
                temp = 0
            else:
                temp = temp * 10 + digit
    result += temp
    return result

然后在关键词匹配的方法中实现调整音量

        if "音量" in res:
            volumn=res.split('百分之')[1]
            volumn=hanzi_to_number_pinyin(volumn)
            print(volumn)
            mw.volumeSet(volumn)

然后在我们的主方法里面,开启一个新线程,进行调用即可

image-20241205173514908

image-20241205173702632

以下是全部代码:

# weekupm.py
import json
import time

import pyaudio
import numpy as np
from vosk import Model, KaldiRecognizer, SetLogLevel
from baidu_tts import baidu_tts_test

dir_path={
        '浏览器': "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
        '记事本': "C:\\Windows\\System32\\notepad.exe",
        '计算器': "C:\\Windows\\System32\\calc.exe"
    }
def SaveWave(model):
    # 设置音频参数
    FORMAT = pyaudio.paInt16    # 音频流的格式
    RATE = 44100                # 采样率,单位Hz
    CHUNK = 4000                # 单位帧
    THRESHOLDNUM = 10           # 静默时间,超过这个个数就保存文件
    THRESHOLD = 100             # 设定停止采集阈值

    audio = pyaudio.PyAudio()
    stream = audio.open(format=FORMAT,
                        channels=1,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)
    frames = []
    print("开始录音...")
    count = 0
    while count < THRESHOLDNUM:
        data = stream.read(CHUNK, exception_on_overflow=False)
        np_data = np.frombuffer(data, dtype=np.int16)
        frame_energy = np.mean(np.abs(np_data))
        # print(frame_energy)
        # 如果能量低于阈值持续时间过长,则停止录音
        if frame_energy < THRESHOLD:
            count += 1
        elif count > 0:
            count -= 1

        frames.append(data)
    print("停止录音!")
    stream.stop_stream()
    stream.close()
    audio.terminate()

    rec = KaldiRecognizer(model, RATE)
    rec.SetWords(True)
    str_ret = ""
    for data in frames:
        if rec.AcceptWaveform(data):
            result = json.loads(rec.Result())
            if 'text' in result:
                str_ret += result['text']

    result = json.loads(rec.FinalResult())
    if 'text' in result:
        str_ret += result['text']

    str_ret = "".join(str_ret.split())
    return str_ret

def answermethod(answer):
    baidu_tts_test(answer)
    time.sleep(1)
def weekup(mw):
    model = Model("vosk-model-cn-0.15")
    SetLogLevel(-1)

    while 1:
        res = SaveWave(model)
        if res != "" and res != None:
            print(res)
        if "小爱同学" in res:
            answer = "我在"
            answermethod(answer)

        if "聊天" in res:
            answer = "好的,请开始"
            answermethod(answer)
            from stt import run as stt_run
            result=stt_run()
        if "打开" in res:
            app_name=res.replace("打开",'').strip()
            if app_name in dir_path:
                app_path=dir_path[app_name]
                import subprocess
                subprocess.Popen(app_path)
                answer = "好的,已经为您打开了"
                answermethod(answer)
        if "歌" in res:
            from testyuncloud import yuncloudMW
            mw.playMusic()
            # print("播放歌曲")
        if "暂停" in res:
            mw.playMusic()
        if "上一首" in res:
            mw.prevMusic()
        if "下一首" in res:
            mw.nextMusic()
        if "音量" in res:
            volumn=res.split('百分之')[1]
            volumn=hanzi_to_number_pinyin(volumn)
            print(volumn)
            mw.volumeSet(volumn)


from pypinyin import lazy_pinyin

def hanzi_to_number_pinyin(hanzi_str):
    hnd = {
        'ling': 0, 'yi': 1, 'er': 2, 'san': 3, 'si': 4, 'wu': 5, 'liu': 6, 'qi': 7, 'ba': 8, 'jiu': 9,
        'shi': 10, 'bai': 100, 'qian': 1000, 'wan': 10000, 'yi': 100000000
    }
    pinyin_list = lazy_pinyin(hanzi_str)
    result = 0
    temp = 0
    for pinyin in pinyin_list:
        if pinyin in hnd:
            digit = hnd[pinyin]
            if digit >= 10:
                if temp == 0:
                    temp = 1
                result += temp * digit
                temp = 0
            else:
                temp = temp * 10 + digit
    result += temp
    return result


# baidu_tts.py
# coding=utf-8
import sys
import json
import pygame
import time
import io
IS_PY3 = sys.version_info.major == 3
if IS_PY3:
    from urllib.request import urlopen
    from urllib.request import Request
    from urllib.error import URLError
    from urllib.parse import urlencode
    from urllib.parse import quote_plus
else:
    import urllib2
    from urllib import quote_plus
    from urllib2 import urlopen
    from urllib2 import Request
    from urllib2 import URLError
    from urllib import urlencode

API_KEY = '2I46jXf5CmlNycyOVAETigud'
SECRET_KEY = 'IJu44Y0K09y93ZQAOsObkJEhOEd1NBQa'

# TEXT = "欢迎使用百度语音合成。"

# 发音人选择, 基础音库:0为度小美,1为度小宇,3为度逍遥,4为度丫丫,
# 精品音库:5为度小娇,103为度米朵,106为度博文,110为度小童,111为度小萌,默认为度小美 
PER = 4
# 语速,取值0-15,默认为5中语速
SPD = 5
# 音调,取值0-15,默认为5中语调
PIT = 5
# 音量,取值0-9,默认为5中音量
VOL = 5
# 下载的文件格式, 3:mp3(default) 4: pcm-16k 5: pcm-8k 6. wav
AUE = 3

FORMATS = {3: "mp3", 4: "pcm", 5: "pcm", 6: "wav"}
FORMAT = FORMATS[AUE]

CUID = "123456PYTHON"

TTS_URL = 'http://tsn.baidu.com/text2audio'


class DemoError(Exception):
    pass


"""  TOKEN start """

TOKEN_URL = 'http://aip.baidubce.com/oauth/2.0/token'
SCOPE = 'audio_tts_post'  # 有此scope表示有tts能力,没有请在网页里勾选


def fetch_token():
    print("fetch token begin")
    params = {'grant_type': 'client_credentials',
              'client_id': API_KEY,
              'client_secret': SECRET_KEY}
    post_data = urlencode(params)
    if (IS_PY3):
        post_data = post_data.encode('utf-8')
    req = Request(TOKEN_URL, post_data)
    try:
        f = urlopen(req, timeout=5)
        result_str = f.read()
    except URLError as err:
        print('token http response http code : ' + str(err.code))
        result_str = err.read()
    if (IS_PY3):
        result_str = result_str.decode()

    print(result_str)
    result = json.loads(result_str)
    print(result)
    if ('access_token' in result.keys() and 'scope' in result.keys()):
        if not SCOPE in result['scope'].split(' '):
            raise DemoError('scope is not correct')
        print('SUCCESS WITH TOKEN: %s ; EXPIRES IN SECONDS: %s' % (result['access_token'], result['expires_in']))
        return result['access_token']
    else:
        raise DemoError('MAYBE API_KEY or SECRET_KEY not correct: access_token or scope not found in token response')


"""  TOKEN end """

def baidu_tts_test(text,PER=0,SPD=10,PIT=PIT,VOL=VOL):
    token = fetch_token()
    tex = quote_plus(text)  # 此处TEXT需要两次urlencode
    print(tex)
    params = {'tok': token, 'tex': tex, 'per': PER, 'spd': SPD, 'pit': PIT, 'vol': VOL, 'aue': AUE, 'cuid': CUID,
              'lan': 'zh', 'ctp': 1}  # lan ctp 固定参数

    data = urlencode(params)
    print('test on Web Browser' + TTS_URL + '?' + data)

    req = Request(TTS_URL, data.encode('utf-8'))
    has_error = False
    try:
        f = urlopen(req)
        result_str = f.read()

        headers = dict((name.lower(), value) for name, value in f.headers.items())

        has_error = ('content-type' not in headers.keys() or headers['content-type'].find('audio/') < 0)
    except  URLError as err:
        print('asr http response http code : ' + str(err.code))
        result_str = err.read()
        has_error = True

    save_file = "error.txt" if has_error else 'result.' + FORMAT
    with open(save_file, 'wb') as of:
        of.write(result_str)

    if has_error:
        if (IS_PY3):
            result_str = str(result_str, 'utf-8')
        print("tts api  error:" + result_str)
    else:
        # 初始化pygame
        pygame.mixer.init()

        # 将音频数据加载到内存
        pygame.mixer.music.load(io.BytesIO(result_str))

        # 播放音频
        pygame.mixer.music.play()

        # # 防止程序立即退出
        # while pygame.mixer.music.get_busy():
        #     time.sleep(1)
    print("result saved as :" + save_file)
<think>好的,我需要帮助用户了解如何安装Cursor以及注册账号。首先,根据用户提供的引用内容,我需要整理出安装和注册的具体步骤,并确保符合用户的要求:逐步详细引导,每次只完成一步,使用中文,适合Windows系统和PowerShell用户。 首先,安装步骤。根据引用[1],用户需要访问官网下载安装包,然后以管理员身份运行安装程序。引用[3]提到要详细解释每一步,所以需要分步说明如何下载、运行安装程序,并可能包括截图或图示的提示。要注意提醒用户使用管理员权限,避免安装失败。 然后是账号注册部分。引用[1]和[2]提到运行Cursor后的账户设置,可能需要创建账号或登录。需要指导用户打开软件后如何找到注册入口,填写信息,验证邮箱等步骤。这里要确保每一步都清晰,比如点击哪个按钮,输入哪些信息,如何处理验证邮件。 还要注意引用中的细节,比如引用[3]强调用户是小白,所以语言要尽量简单,避免专业术语,必要时解释为什么要执行某一步骤,比如为什么需要管理员权限。同时,检查用户是否完成每一步,比如询问用户是否成功下载,是否安装完成等,但用户的问题只是请求程,可能不需要交互式检查,但回答中可能需要提示用户确认。 另外,要确保LaTeX格式正确,比如所有数学表达式用$...$,独立公式用$$...$$,但当前内容可能不涉及数学公式,所以可能不需要。但需要检查是否有其他格式要求,如代码块用三个反引号包裹,正确的引用标识等。 最后,生成相关问题,可能涉及安装失败的处理,账号登录问题,或软件功能的使用。根据引用中的内容,可能的问题包括如何解决安装时的机器码问题,如何配置账户设置等。 需要确保回答结构清晰,分步骤,使用有序列表,代码块正确,引用标识正确放置在每个引用的末尾。同时,用户要求使用中文,避免任何Markdown格式错误,比如正确的代码块标注语言,如python或powershell。 现在,开始组织回答内容,分安装步骤和注册步骤,每步详细说明,然后生成相关问题。需要引用正确的来源,比如引用[1]和[3]主要涉及安装和注册,引用[2]可能更多关于使用程,但用户的问题集中在安装和注册,所以重点引用1和3。</think>### Cursor安装与账号注册程 #### 一、安装步骤 1. **访问官网下载安装包** 打开浏览器,访问[Cursor官网](https://www.cursor.so/),点击页面上的“Download”按钮,选择Windows版本下载[^1]。 2. **以管理员身份运行安装程序** 在下载文件夹中找到安装文件(如`CursorSetup.exe`),右键点击文件,选择“以管理员身份运行”(避免权限问题导致安装失败)[^3]。 3. **按照安装向导操作** - 安装界面会提示选择安装路径,默认路径为`C:\Program Files\Cursor`,可保持默认或自定义路径。 - 勾选“创建桌面快捷方式”(方便后续快速启动)。 - 点击“Next”直至完成安装。 4. **首次启动Cursor** 双击桌面快捷方式启动Cursor。若系统提示安全警告,选择“允许运行”。 --- #### 二、账号注册与登录 1. **进入账户设置界面** 启动Cursor后,点击界面右上角的“Sign In”按钮,进入登录/注册页面。 2. **注册新账号** - 点击“Create Account”或类似选项。 - 填写邮箱地址(如`yourname@example.com`)、设置密码(需包含大小写字母和数字)。 - 勾选同意用户协议,点击“Sign Up”[^2]。 3. **验证邮箱** 打开注册时使用的邮箱,找到Cursor发送的验证邮件,点击邮件中的链接完成验证。 4. **登录账号** 返回Cursor界面,输入已验证的邮箱和密码,点击“Sign In”即可使用全部功能。 --- #### 三、验证安装与注册成功 - **检查安装**:在PowerShell中输入以下命令查看版本(需关闭Cursor后执行): ```powershell Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-Object DisplayName, DisplayVersion | Where-Object {$_.DisplayName -like "*Cursor*"} ``` 若输出类似`Cursor 1.0.0`,则安装成功。 - **检查登录状态**:登录后界面右上角会显示账号邮箱,表示注册成功[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值