speaking_assessment.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # -*- coding: utf-8 -*-
  2. import sys
  3. import hmac
  4. import hashlib
  5. import base64
  6. import time
  7. import json
  8. import threading
  9. import urllib
  10. import websocket
  11. import uuid
  12. from urllib.parse import quote
  13. from tools.loglog import logger
  14. def is_python3():
  15. if sys.version > '3':
  16. return True
  17. return False
  18. class SpeakingAssessmentListener():
  19. '''
  20. reponse:
  21. on_recognition_start的返回只有voice_id字段。
  22. on_fail 只有voice_id、code、message字段。
  23. on_recognition_complete没有result字段。
  24. 其余消息包含所有字段。
  25. 字段名 类型
  26. code Integer
  27. message String
  28. voice_id String
  29. message_id String
  30. result
  31. final Integer
  32. #
  33. '''
  34. def on_recognition_start(self, response):
  35. pass
  36. def on_intermediate_result(self, response):
  37. pass
  38. def on_recognition_complete(self, response):
  39. pass
  40. def on_fail(self, response):
  41. pass
  42. NOTOPEN = 0
  43. STARTED = 1
  44. OPENED = 2
  45. FINAL = 3
  46. ERROR = 4
  47. CLOSED = 5
  48. def quote_autho(autho):
  49. if sys.version_info >= (3, 0):
  50. import urllib.parse as urlparse
  51. return urlparse.quote(autho)
  52. else:
  53. return urllib.quote(autho)
  54. class SpeakingAssessment:
  55. def __init__(self, appid, credential, engine_model_type, listener):
  56. self.result = ""
  57. self.credential = credential
  58. self.appid = appid
  59. self.server_engine_type = engine_model_type
  60. self.status = NOTOPEN
  61. self.ws = None
  62. self.wst = None
  63. self.voice_id = ""
  64. self.new_start = 0
  65. self.listener = listener
  66. self.text_mode = 0
  67. self.ref_text = ""
  68. self.keyword = ""
  69. self.eval_mode = 0
  70. self.score_coeff = 1.0
  71. self.sentence_info_enabled = 0
  72. self.voice_format = 0
  73. self.nonce = ""
  74. self.rec_mode = 0
  75. def set_text_mode(self, text_mode):
  76. self.text_mode = text_mode
  77. def set_rec_mode(self, rec_mode):
  78. self.rec_mode = rec_mode
  79. def set_ref_text(self, ref_text):
  80. self.ref_text = ref_text
  81. def set_keyword(self, keyword):
  82. self.keyword = keyword
  83. def set_eval_mode(self, eval_mode):
  84. self.eval_mode = eval_mode
  85. def set_sentence_info_enabled(self, sentence_info_enabled):
  86. self.sentence_info_enabled = sentence_info_enabled
  87. def set_voice_format(self, voice_format):
  88. self.voice_format = voice_format
  89. def set_nonce(self, nonce):
  90. self.nonce = nonce
  91. def format_sign_string(self, param):
  92. signstr = "soe.cloud.tencent.com/soe/api/"
  93. for t in param:
  94. if 'appid' in t:
  95. signstr += str(t[1])
  96. break
  97. signstr += "?"
  98. for x in param:
  99. tmp = x
  100. if 'appid' in x:
  101. continue
  102. for t in tmp:
  103. signstr += str(t)
  104. signstr += "="
  105. signstr = signstr[:-1]
  106. signstr += "&"
  107. signstr = signstr[:-1]
  108. return signstr
  109. def create_query_string(self, param):
  110. signstr = ""
  111. for key, value in param.items():
  112. if key == 'appid':
  113. signstr += str(value)
  114. break
  115. signstr += "?"
  116. for key, value in param.items():
  117. if key == 'appid':
  118. continue
  119. value = quote_autho(str(value))
  120. signstr += str(key) + "=" + str(value) + "&"
  121. signstr = signstr[:-1]
  122. return "wss://soe.cloud.tencent.com/soe/api/" + signstr
  123. def sign(self, signstr, secret_key):
  124. hmacstr = hmac.new(secret_key.encode('utf-8'),
  125. signstr.encode('utf-8'), hashlib.sha1).digest()
  126. s = base64.b64encode(hmacstr)
  127. s = s.decode('utf-8')
  128. return s
  129. def create_query_arr(self):
  130. query_arr = dict()
  131. query_arr['appid'] = self.appid
  132. query_arr['server_engine_type'] = self.server_engine_type
  133. query_arr['text_mode'] = self.text_mode
  134. query_arr['rec_mode'] = self.rec_mode
  135. query_arr['ref_text'] = self.ref_text
  136. query_arr['keyword'] = self.keyword
  137. query_arr['eval_mode'] = self.eval_mode
  138. query_arr['score_coeff'] = self.score_coeff
  139. query_arr['sentence_info_enabled'] = self.sentence_info_enabled
  140. query_arr['secretid'] = self.credential.secret_id
  141. if self.credential.token != "":
  142. query_arr['token'] = self.credential.token
  143. query_arr['voice_format'] = self.voice_format
  144. query_arr['voice_id'] = self.voice_id
  145. query_arr['timestamp'] = str(int(time.time()))
  146. if self.nonce != "":
  147. query_arr['nonce'] = self.nonce
  148. else:
  149. query_arr['nonce'] = query_arr['timestamp']
  150. query_arr['expired'] = int(time.time()) + 24 * 60 * 60
  151. return query_arr
  152. def stop(self):
  153. if self.status == OPENED:
  154. msg = {'type': "end"}
  155. text_str = json.dumps(msg)
  156. self.ws.sock.send(text_str)
  157. if self.ws:
  158. if self.wst and self.wst.is_alive():
  159. self.wst.join()
  160. self.ws.close()
  161. def write(self, data):
  162. while self.status == STARTED:
  163. time.sleep(0.1)
  164. if self.status == OPENED:
  165. self.ws.sock.send_binary(data)
  166. def start(self):
  167. def on_message(ws, message):
  168. response = json.loads(message)
  169. response['voice_id'] = self.voice_id
  170. if response['code'] != 0:
  171. logger.error("%s server recognition fail %s" %
  172. (response['voice_id'], response['message']))
  173. self.listener.on_fail(response)
  174. return
  175. if "final" in response and response["final"] == 1:
  176. self.status = FINAL
  177. self.result = message
  178. self.listener.on_recognition_complete(response)
  179. self.ws.close()
  180. return
  181. else:
  182. if response["result"] is not None:
  183. self.listener.on_intermediate_result(response)
  184. logger.info("%s recognition doing" % response['voice_id'])
  185. return
  186. def on_error(ws, error):
  187. if self.status == FINAL:
  188. return
  189. logger.error("websocket error %s voice id %s" %
  190. (format(error), self.voice_id))
  191. self.status = ERROR
  192. def on_close(ws, close_status_code, close_msg):
  193. self.status = CLOSED
  194. logger.info("websocket closed voice id %s" %
  195. self.voice_id)
  196. def on_open(ws):
  197. self.status = OPENED
  198. query_arr = self.create_query_arr()
  199. if self.voice_id == "":
  200. query_arr['voice_id'] = str(uuid.uuid1())
  201. self.voice_id = query_arr['voice_id']
  202. query = sorted(query_arr.items(), key=lambda d: d[0])
  203. signstr = self.format_sign_string(query)
  204. autho = self.sign(signstr, self.credential.secret_key)
  205. requrl = self.create_query_string(query_arr)
  206. if is_python3():
  207. autho = urllib.parse.quote(autho)
  208. else:
  209. autho = urllib.quote(autho)
  210. requrl += "&signature=%s" % autho
  211. self.ws = websocket.WebSocketApp(requrl, None,
  212. on_error=on_error, on_close=on_close, on_message=on_message)
  213. self.ws.on_open = on_open
  214. self.wst = threading.Thread(target=self.ws.run_forever)
  215. self.wst.daemon = True
  216. self.wst.start()
  217. self.status = STARTED
  218. response = {'voice_id': self.voice_id}
  219. self.listener.on_recognition_start(response)