voicetext.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 
00003 import rospy
00004 import json
00005 import traceback
00006 import requests
00007 from requests.auth import HTTPBasicAuth
00008 
00009 # import library
00010 from rospeex_core import logging_util
00011 from rospeex_core.validators import accepts
00012 from rospeex_core.validators import check_language
00013 from rospeex_core import exceptions as ext
00014 from rospeex_core.ss.base import IClient
00015 from rospeex_core.ss import nict
00016 
00017 
00018 logger = logging_util.get_logger(__name__)
00019 
00020 
00021 class Client(IClient):
00022     """ SpeechSynthesisCient_VoiceText class """
00023     LANGUAGES = ['ja']
00024 
00025     # voicetext parameters
00026     VERSION = "v1"
00027     URL = "https://api.voicetext.jp/%s/tts" % VERSION
00028     SPEAKER_LIST = ['show', 'haruka', 'hikari', 'takeru', 'santa', 'bear', '*']
00029     EMOTION_LIST = ['heppiness', 'anger', 'sadness']
00030     EMOTION_SPEAKER = ['haruka', 'hikari', 'takeru', 'santa', 'bear']
00031     EMOTION_RANGE = [1, 2]
00032     TEXT_RANGE = [0, 200]
00033     PITCH_RANGE = [50, 200]
00034     SPEED_RANGE = [50, 400]
00035     VOLUME_RANGE = [50, 200]
00036     DEFAULT_VOICE_FONT = 'show'
00037 
00038     def __init__(self):
00039         """ init function
00040         @param args: parameter list for initialize
00041         @type args: list
00042         @param kwargs: parameter dictionary for initialize
00043         """
00044         self._key = ''
00045         self._pitch = 100
00046         self._speed = 100
00047         self._volume = 100
00048         self._emotion = None
00049         self._emotion_level = 1
00050         self._load_parameter()
00051 
00052     def _load_parameter(self):
00053         """ load parameter from rospy """
00054         try:
00055             self._key = rospy.get_param('~voicetext_api_key', '')
00056             self._pitch = rospy.get_param('~voicetext_pitch', 100)
00057             self._speed = rospy.get_param('~voicetext_speed', 100)
00058             self._volume = rospy.get_param('~voicetext_volume', 100)
00059             self._emotion = rospy.get_param('~voicetext_emotion', None)
00060             self._emotion_level = rospy.get_param(
00061                 '~voicetext_emotion_level',
00062                 1
00063             )
00064 
00065         except Exception:
00066             pass
00067 
00068     @accepts(message=basestring, language=str, voice_font=str, timeout=int)
00069     def request(self, message, language='ja', voice_font='show', timeout=10):
00070         """
00071         Send speech synthesis request to server,
00072         and get speech synthesis result.
00073         @param message: message
00074         @type  message: str
00075         @param language: speech synthesis language
00076         @type  language: str
00077         @param voice_font: taraget voice font
00078         @type  voice_font: str
00079         @param timeout: request timeout time (second)
00080         @type  timeout: float
00081         @return: voice data (wav format binary)
00082         @rtype: str
00083         @raise SpeechSynthesisException:
00084         """
00085         # check language
00086         check_language(language, self.LANGUAGES)
00087 
00088         # check api key
00089         self._check_api_key()
00090 
00091         # check speaker
00092         self._check_voice_font(voice_font)
00093         if voice_font == '*':
00094             voice_font = self.DEFAULT_VOICE_FONT
00095 
00096         # check emotion
00097         self._check_emotion(voice_font)
00098 
00099         # check text / pitch / speed / volume
00100         self._check_text(message)
00101         self._check_pitch()
00102         self._check_speed()
00103         self._check_volume()
00104 
00105         # send data
00106         try:
00107             client = nict.Client()
00108             client.request(message, language, '*', 10)
00109         except Exception:
00110             pass
00111 
00112         # create parameters
00113         params = {
00114             'text': message,
00115             'speaker': voice_font,
00116             'pitch': self._pitch,
00117             'speed': self._speed,
00118             'volume': self._volume
00119         }
00120 
00121         try:
00122             auth = HTTPBasicAuth(self._key, '')
00123             response = requests.post(
00124                 self.URL,
00125                 params=params,
00126                 auth=auth,
00127                 timeout=timeout
00128             )
00129 
00130         except requests.exceptions.Timeout as err:
00131             msg = 'request time out. Exception: {err}'.format(
00132                 err=str(err)
00133             )
00134             raise ext.RequestTimeoutException(msg)
00135 
00136         except requests.exceptions.ConnectionError as err:
00137             msg = 'network connection error. Exception: {err}'.format(
00138                 err=str(err)
00139             )
00140             raise ext.InvalidRequestException(msg)
00141 
00142         except requests.exceptions.RequestException as err:
00143             msg = 'invalid request error. Exception: {err}'.format(
00144                 err=str(err)
00145             )
00146             raise ext.InvalidRequestException(msg)
00147 
00148         # check response
00149         if response.status_code == 200:
00150             return response.content
00151 
00152         else:
00153             content = json.loads(response.content)
00154             self._check_errors(response.status_code, content)
00155 
00156     def _check_api_key(self):
00157         """ check api key
00158         @raises ParameterException
00159         """
00160         if self._key is None or self._key == '':
00161             msg = 'parameter failed. if you want to use voicetext engine'\
00162                    'you MUST set api key for voicetext api.'
00163             raise ext.ParameterException(msg)
00164 
00165     def _check_text(self, text):
00166         """ check tts text
00167         @param text: text
00168         @type str
00169         @raises ParameterException
00170         """
00171         if not len(text) in range(*self.TEXT_RANGE):
00172             msg = 'parameter failed. text length is not in range.'\
00173                   'Except: {range[0]} <= text_length:{value}'\
00174                   ' <= {range[1]}'.format(
00175                         range=self.TEXT_RANGE,
00176                         value=len(text)
00177                     )
00178             raise ext.ParameterException(msg)
00179 
00180     def _check_voice_font(self, voice_font):
00181         """ check voice font
00182         @param voice_font: voice font
00183         @type voice_font: str
00184         @raises ParameterException
00185         """
00186         if voice_font not in self.SPEAKER_LIST:
00187             msg = 'parameter failed [{voice_font}].'\
00188                   'you choose voice fonts following parametrs '\
00189                   '{speaker_list}'.format(
00190                         voice_font=voice_font,
00191                         speaker_list=str(self.SPEAKER_LIST)
00192                     )
00193             raise ext.ParameterException(msg)
00194 
00195     def _check_emotion(self, voice_font):
00196         """ check emotion and emotion_level
00197         @param voice_font: voice font
00198         @type voice_font: str
00199         @raises ParameterException
00200         """
00201         if self._emotion is None:
00202             return
00203 
00204         if voice_font not in self.EMOTION_SPEAKER:
00205             msg = 'parameter failed. if you want to use emotion option,'\
00206                   'you choose following speakers %s' % (self.SPEAKER_LIST)
00207             raise ext.ParameterException(msg)
00208 
00209         if self._emotion_level not in range(*self.EMOTION_RANGE):
00210             msg = 'parameter failed. emotion level is not in range.'\
00211                   'Except: {range[0]} <= emotion_level:{value}' \
00212                   ' <= {range[1]}'.format(
00213                         range=self.EMOTION_RANGE,
00214                         value=self._emotion_level
00215                     )
00216             raise ext.ParameterException(msg)
00217 
00218     def _check_volume(self):
00219         """ check volume level
00220         @raises ParameterException
00221         """
00222         if self._volume not in range(*self.VOLUME_RANGE):
00223             msg = 'parameter failed. volume level is not in range.'\
00224                   'Except: {range[0]} <= volume:{value} <= {range[1]}'.format(
00225                         range=self.VOLUME_RANGE,
00226                         value=self._volume,
00227                     )
00228             raise ext.ParameterException(msg)
00229 
00230     def _check_speed(self):
00231         """ check speed level
00232         @raises ParameterException
00233         """
00234         if self._speed not in range(*self.SPEED_RANGE):
00235             msg = 'parameter failed. speed level is not in range.'\
00236                   'Except: {range[0]} <= speed:{value} <= {range[1]}'.format(
00237                         range=self.SPEED_RANGE,
00238                         value=self._speed,
00239                     )
00240             raise ext.ParameterException(msg)
00241 
00242     def _check_pitch(self):
00243         """ check speed level
00244         @raises ParameterException
00245         """
00246         if self._pitch not in range(*self.PITCH_RANGE):
00247             msg = 'parameter failed. pitch level is not in range.'\
00248                   'Except: {range[0]} <= pitch:{value} <= {range[1]}'.format(
00249                         range=self.PITCH_RANGE,
00250                         value=self._pitch,
00251                     )
00252             raise ext.ParameterException(msg)
00253 
00254     def _check_errors(self, status_code, content):
00255         """ check server errors
00256         @param status_code: status code
00257         @type  status_code: int
00258         @param content: response message
00259         @type  content: str
00260         @raises InvalidRequestException
00261         @raises InvalidResponseException
00262         """
00263         try:
00264             message = content['error']['message']
00265         except KeyError as err:
00266             msg = 'invalid response. Message: {message}'.format(
00267                 message=str(err)
00268             )
00269             raise ext.InvalidResponseException(msg)
00270 
00271         if status_code == 400:
00272             msg = 'invalid request. Message: {message}'.format(
00273                 message=message
00274             )
00275             raise ext.InvalidRequestException(msg)
00276 
00277         elif status_code == 401:
00278             msg = 'auth failed. Message: {message}'.format(
00279                 message=message
00280             )
00281             raise ext.InvalidRequestException(msg)
00282 
00283         elif status_code == 404:
00284             msg = 'target url is not avariable. Message: {message}'.format(
00285                 message=message
00286             )
00287             raise ext.InvalidRequestException(msg)
00288 
00289         elif status_code == 405:
00290             msg = 'invalid request. Message: {message}'.format(
00291                 message=message
00292             )
00293             raise ext.InvalidRequestException(msg)
00294 
00295         elif status_code == 500:
00296             msg = 'server internal error. Message: {message}'.format(
00297                 message=message
00298             )
00299             raise ext.InvalidResponseException(msg)
00300 
00301         elif status_code == 503:
00302             msg = 'service unavailable error. Message: {message}'.format(
00303                 message=message
00304             )
00305             raise ext.InvalidResponseException(msg)
00306 
00307         else:
00308             msg = 'undefined error. Message: {message} '\
00309                   'Traceback:{trace}'.format(
00310                         message=message,
00311                         trace=traceback.format_exc()
00312                     )
00313             raise ext.InvalidResponseException(msg)


rospeex_core
Author(s): Komei Sugiura
autogenerated on Thu Apr 20 2017 03:08:53