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


rospeex_core
Author(s): Komei Sugiura
autogenerated on Thu Jun 6 2019 18:53:10