選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

102 行
3.9KB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Author: chazzjimel
  4. Email: chazzjimel@gmail.com
  5. wechat:cheung-z-x
  6. Description:
  7. ali voice service
  8. """
  9. import json
  10. import os
  11. import re
  12. import time
  13. from bridge.reply import Reply, ReplyType
  14. from common.log import logger
  15. from voice.audio_convert import get_pcm_from_wav
  16. from voice.voice import Voice
  17. from voice.ali.ali_api import AliyunTokenGenerator, speech_to_text_aliyun, text_to_speech_aliyun
  18. from config import conf
  19. class AliVoice(Voice):
  20. def __init__(self):
  21. """
  22. 初始化AliVoice类,从配置文件加载必要的配置。
  23. """
  24. try:
  25. curdir = os.path.dirname(__file__)
  26. config_path = os.path.join(curdir, "config.json")
  27. with open(config_path, "r") as fr:
  28. config = json.load(fr)
  29. self.token = None
  30. self.token_expire_time = 0
  31. # 默认复用阿里云千问的 access_key 和 access_secret
  32. self.api_url_voice_to_text = config.get("api_url_voice_to_text")
  33. self.api_url_text_to_voice = config.get("api_url_text_to_voice")
  34. self.app_key = config.get("app_key")
  35. self.access_key_id = conf().get("qwen_access_key_id") or config.get("access_key_id")
  36. self.access_key_secret = conf().get("qwen_access_key_secret") or config.get("access_key_secret")
  37. except Exception as e:
  38. logger.warn("AliVoice init failed: %s, ignore " % e)
  39. def textToVoice(self, text):
  40. """
  41. 将文本转换为语音文件。
  42. :param text: 要转换的文本。
  43. :return: 返回一个Reply对象,其中包含转换得到的语音文件或错误信息。
  44. """
  45. # 清除文本中的非中文、非英文和非基本字符
  46. text = re.sub(r'[^\u4e00-\u9fa5\u3040-\u30FF\uAC00-\uD7AFa-zA-Z0-9'
  47. r'äöüÄÖÜáéíóúÁÉÍÓÚàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛçÇñÑ,。!?,.]', '', text)
  48. # 提取有效的token
  49. token_id = self.get_valid_token()
  50. fileName = text_to_speech_aliyun(self.api_url_text_to_voice, text, self.app_key, token_id)
  51. if fileName:
  52. logger.info("[Ali] textToVoice text={} voice file name={}".format(text, fileName))
  53. reply = Reply(ReplyType.VOICE, fileName)
  54. else:
  55. reply = Reply(ReplyType.ERROR, "抱歉,语音合成失败")
  56. return reply
  57. def voiceToText(self, voice_file):
  58. """
  59. 将语音文件转换为文本。
  60. :param voice_file: 要转换的语音文件。
  61. :return: 返回一个Reply对象,其中包含转换得到的文本或错误信息。
  62. """
  63. # 提取有效的token
  64. token_id = self.get_valid_token()
  65. logger.debug("[Ali] voice file name={}".format(voice_file))
  66. pcm = get_pcm_from_wav(voice_file)
  67. text = speech_to_text_aliyun(self.api_url_voice_to_text, pcm, self.app_key, token_id)
  68. if text:
  69. logger.info("[Ali] VoicetoText = {}".format(text))
  70. reply = Reply(ReplyType.TEXT, text)
  71. else:
  72. reply = Reply(ReplyType.ERROR, "抱歉,语音识别失败")
  73. return reply
  74. def get_valid_token(self):
  75. """
  76. 获取有效的阿里云token。
  77. :return: 返回有效的token字符串。
  78. """
  79. current_time = time.time()
  80. if self.token is None or current_time >= self.token_expire_time:
  81. get_token = AliyunTokenGenerator(self.access_key_id, self.access_key_secret)
  82. token_str = get_token.get_token()
  83. token_data = json.loads(token_str)
  84. self.token = token_data["Token"]["Id"]
  85. # 将过期时间减少一小段时间(例如5分钟),以避免在边界条件下的过期
  86. self.token_expire_time = token_data["Token"]["ExpireTime"] - 300
  87. logger.debug(f"新获取的阿里云token:{self.token}")
  88. else:
  89. logger.debug("使用缓存的token")
  90. return self.token