docomo_aitalk.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 
00003 import rospy
00004 import traceback
00005 import requests
00006 
00007 import wave
00008 import struct
00009 import StringIO
00010 
00011 # import library
00012 from rospeex_core import logging_util
00013 from rospeex_core.validators import accepts
00014 from rospeex_core.validators import check_language
00015 from rospeex_core import exceptions as ext
00016 from rospeex_core.ss.base import IClient
00017 from rospeex_core.ss import nict
00018 
00019 
00020 logger = logging_util.get_logger(__name__)
00021 
00022 
00023 class Client(IClient):
00024     """ SpeechSynthesisCient_docomo_aitalk class """
00025     LANGUAGES = ['ja']
00026 
00027     # docomo_aitalk parameters
00028     VERSION = "v1"
00029     URL = "https://api.apigw.smt.docomo.ne.jp/aiTalk/%s/textToSpeech" % VERSION
00030 
00031     SPEAKER_LIST = ['nozomi','seiji','akari','anzu','hiroshi','kaho','koutarou','maki','nanako','osamu','sumire', '*']
00032     TEXT_RANGE = [0, 200]
00033 
00034     DEFAULT_VOICE_FONT = 'nozomi'
00035 
00036     def __init__(self):
00037         """ init function
00038         @param args: parameter list for initialize
00039         @type args: list
00040         @param kwargs: parameter dictionary for initialize
00041         """
00042         self._key = ''
00043         self._speakerid  = 0
00044         self._speechrate = 1.0
00045         self._powerrate  = 1.0
00046         self._voicetype  = 0
00047         self._audiofileformat  = 0
00048         self._load_parameter()
00049 
00050     def _load_parameter(self):
00051         """ load parameter from rospy """
00052         try:
00053             self._key = rospy.get_param('~docomo_api_key', '')
00054         except Exception:
00055             pass
00056 
00057     @accepts(message=basestring, language=str, voice_font=str, timeout=int)
00058     def request(self, message, language='ja', voice_font='nozomi', timeout=10):
00059         """
00060         Send speech synthesis request to server,
00061         and get speech synthesis result.
00062         @param message: message
00063         @type  message: str
00064         @param language: speech synthesis language
00065         @type  language: str
00066         @param voice_font: taraget voice font
00067         @type  voice_font: str
00068         @param timeout: request timeout time (second)
00069         @type  timeout: float
00070         @return: voice data (wav format binary)
00071         @rtype: str
00072         @raise SpeechSynthesisException:
00073         """
00074         # check language
00075         check_language(language, self.LANGUAGES)
00076 
00077         # check api key
00078         self._check_api_key()
00079 
00080         # check speaker
00081         self._check_voice_font(voice_font)
00082         if voice_font == '*':
00083             voice_font = self.DEFAULT_VOICE_FONT
00084 
00085         # check text
00086         self._check_text(message)
00087 
00088         # send data
00089         try:
00090             client = nict.Client()
00091             client.request(message, language, '*', 10)
00092         except Exception:
00093             pass
00094 
00095         # create ssml request
00096         template = u"<?xml version='1.0' encoding='utf-8' ?>"\
00097             "<speak version='1.1'><voice name='{font}'>{message}</voice></speak>"
00098 
00099         self._ssml_text = template.format(
00100             font=voice_font,
00101             message=message.decode('utf-8')
00102         )
00103 
00104         # create parameters
00105         headers = {
00106             'Content-Type': 'application/ssml+xml',
00107             'Accept': 'audio/L16'
00108         }
00109 
00110         params = {
00111             'APIKEY': self._key,
00112         }
00113 
00114         try:
00115             response = requests.post(
00116                 self.URL,
00117                 headers=headers,
00118                 data=self._ssml_text.encode('utf-8'),
00119                 params=params,
00120                 timeout=timeout,
00121             )
00122 
00123         except requests.exceptions.Timeout as err:
00124             msg = 'request time out. Exception: {err}'.format(
00125                 err=str(err)
00126             )
00127             raise ext.RequestTimeoutException(msg)
00128 
00129         except requests.exceptions.ConnectionError as err:
00130             msg = 'network connection error. Exception: {err}'.format(
00131                 err=str(err)
00132             )
00133             raise ext.InvalidRequestException(msg)
00134 
00135         except requests.exceptions.RequestException as err:
00136             msg = 'invalid request error. Exception: {err}'.format(
00137                 err=str(err)
00138             )
00139             raise ext.InvalidRequestException(msg)
00140 
00141         # check response
00142         if response.status_code == 200:
00143             wav_buffer = self._create_wav_buffer(response.content)
00144             return wav_buffer
00145 
00146         else:
00147             self._check_errors(response.status_code)
00148 
00149     def _check_api_key(self):
00150         """ check api key
00151         @raises ParameterException
00152         """
00153         if self._key is None or self._key == '':
00154             msg = 'parameter failed. if you want to use docomo_aitalk engine'\
00155                    'you MUST set api key for docomo_aitalk api.'
00156             raise ext.ParameterException(msg)
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}'\
00167                   ' <= {range[1]}'.format(
00168                         range=self.TEXT_RANGE,
00169                         value=len(text)
00170                     )
00171             raise ext.ParameterException(msg)
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 voice_font not in self.SPEAKER_LIST:
00180             msg = 'parameter failed [{voice_font}].'\
00181                   'you choose voice fonts following parametrs '\
00182                   '{speaker_list}'.format(
00183                         voice_font=voice_font,
00184                         speaker_list=str(self.SPEAKER_LIST)
00185                     )
00186             raise ext.ParameterException(msg)
00187 
00188     def _check_errors(self, status_code):
00189         """ check server errors
00190         @param status_code: status code
00191         @type  status_code: int
00192         @raises InvalidRequestException
00193         @raises InvalidResponseException
00194         """
00195 
00196         if status_code == 400:
00197             msg = 'invalid request. StatusCode: {status_code}'.format(
00198                 status_code=status_code
00199             )
00200             raise ext.InvalidRequestException(msg)
00201 
00202         elif status_code == 401:
00203             msg = 'auth failed. StatusCode: {status_code}'.format(
00204                 status_code=status_code
00205             )
00206             raise ext.InvalidRequestException(msg)
00207 
00208         elif status_code == 404:
00209             msg = 'target url is not avariable. StatusCode: {status_code}'.format(
00210                 status_code=status_code
00211             )
00212             raise ext.InvalidRequestException(msg)
00213 
00214         elif status_code == 405:
00215             msg = 'invalid request. StatusCode: {status_code}'.format(
00216                 status_code=status_code
00217             )
00218             raise ext.InvalidRequestException(msg)
00219 
00220         elif status_code == 500:
00221             msg = 'server internal error. StatusCode: {status_code}'.format(
00222                 status_code=status_code
00223             )
00224             raise ext.InvalidResponseException(msg)
00225 
00226         elif status_code == 503:
00227             msg = 'service unavailable error. StatusCode: {status_code}'.format(
00228                 status_code=status_code
00229             )
00230             raise ext.InvalidResponseException(msg)
00231 
00232         else:
00233             msg = 'undefined error. StatusCode: {status_code} '\
00234                   'Traceback:{trace}'.format(
00235                         status_code=status_code,
00236                         trace=traceback.format_exc()
00237                     )
00238             raise ext.InvalidResponseException(msg)
00239 
00240     def _create_wav_buffer(self, content):
00241         """ create wave buffer
00242         @param content: binary data
00243         @type  content: str
00244         @raises WaveConvertException
00245         """
00246 
00247         if content is None or content == '':
00248             msg = 'wave convert failed. input binary data is null.'
00249             raise ext.WaveConvertException(msg)
00250 
00251         try:
00252             buffer = StringIO.StringIO()
00253             
00254             w = wave.open(buffer, 'w')
00255 
00256             buf_len = len(content) / 2 
00257             fmt = '<%dH' % buf_len
00258             unpacked_buf = struct.unpack(fmt, content)
00259             
00260             fmt = '>%dH' % buf_len
00261             packed_buf = struct.pack(fmt, *unpacked_buf)
00262 
00263             param = (1, 2, 16000, len(content), 'NONE', 'not compressed')
00264             w.setparams(param)
00265             w.writeframes(packed_buf)
00266             buffer.flush()
00267 
00268             if buffer is None or buffer == '':
00269                 msg = 'wave convert failed. converted binary data is null.'
00270                 raise ext.WaveConvertException(msg)
00271 
00272             wav_buffer = buffer.getvalue()
00273 
00274             w.close()
00275             buffer.close()
00276 
00277         except Exception as err:
00278             msg = 'wave convert failed.{err}'.format(
00279                 err=str(err)
00280             )
00281             raise ext.WaveConvertException(msg)
00282             
00283         return wav_buffer


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