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


rospeex_core
Author(s): Komei Sugiura
autogenerated on Wed Aug 26 2015 16:10:30