def tts(text, sconf=None, voice=None, language=None): """ Until we support multiple speech configs use the current active one. Note if sconf parameter is not set by caller we hit the db. The caller should cache the speech config where possible. """ say_or_play = None try: sconf = sconf or get_active_speech_config() if sconf: try: path, stat = sconf.get_or_create_voice_path(text) if path != None: abs_uri = '://'.join([settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse('MHLogin.DoctorCom.speech.views.get_voice_clip', kwargs=path) say_or_play = Play(urlparse.urljoin(abs_uri, url)) else: logger.error("Path is null, details: %s" % str(stat)) except NotImplementedError as nie: logger.error("Most likely calling base class: %s" % str(nie)) else: logger.info("Primary speech config not configured, using default") except Exception as err: # enclose in try/catch Exception for now logger.critical("Unexpected, bug: %s" % str(err)) say_or_play = say_or_play or Say(text, voice=voice) logger.info("Returning say or play: %s" % str(say_or_play)) return say_or_play
def tts(text, sconf=None, voice=None, language=None): """ Until we support multiple speech configs use the current active one. Note if sconf parameter is not set by caller we hit the db. The caller should cache the speech config where possible. """ say_or_play = None try: sconf = sconf or get_active_speech_config() if sconf: try: path, stat = sconf.get_or_create_voice_path(text) if path != None: abs_uri = '://'.join( [settings.SERVER_PROTOCOL, settings.SERVER_ADDRESS]) url = reverse( 'MHLogin.DoctorCom.speech.views.get_voice_clip', kwargs=path) say_or_play = Play(urlparse.urljoin(abs_uri, url)) else: logger.error("Path is null, details: %s" % str(stat)) except NotImplementedError as nie: logger.error("Most likely calling base class: %s" % str(nie)) else: logger.info("Primary speech config not configured, using default") except Exception as err: # enclose in try/catch Exception for now logger.critical("Unexpected, bug: %s" % str(err)) say_or_play = say_or_play or Say(text, voice=voice) logger.info("Returning say or play: %s" % str(say_or_play)) return say_or_play
def get_server_status(self): """ Request status from server, should be handled in implmented class. return format is a dictionary { 'status' : <int code>, 'status_text : <any text>, 'data' : None } """ name = sys._getframe().f_code.co_name logger.error("%s not implemented in base-class" % name) raise NotImplementedError("%s needs to be implemented in sub-class" % name)
def post_delete_neospeech_callback(sender, **kwargs): # create transient driver element based db configuration config = kwargs['instance'] config.driver.shutdown() try: # Assume local storage until we have delete() support in utils.storage os.rmdir(os.path.join(settings.MEDIA_ROOT, 'tts', config.name)) except (OSError), oe: # don't recurse delete directory logger.error("Problems removing SpeecConfig: %s, error: %s" % (config.name, str(oe)))
def pre_delete_voiceclip_callback(sender, **kwargs): """ Signal before voiceclip deleted, remove associated media from storage """ # delete media associated with this voiceclip vc = kwargs['instance'] try: # Assume local storage until we have delete() support in utils.storage os.remove( os.path.join(settings.MEDIA_ROOT, 'tts', vc.config.name, vc.filename)) except (OSError), oe: logger.error( "Problems removing VoiceClip: %s, filename empty or not in " "filesystem. Error: %s" % (vc.filename, str(oe)))
def get_or_create_voice_path(self, text): """ Lookup text in voiceclip table, creating entry if needed. By default encoding uses the encoding value in model if encoding paramter is None """ path = stat = None try: vc = VoiceClip.objects.get(config=self, spoken_text=text) vc.save() # access_date field updates when auto_now attribute set # verify existence in fs before moving on verify = get_file(os.path.join('tts', vc.config.name, vc.filename)) if not verify: vc.delete( ) # cleanup clip - occurs when user changes config name raise ObjectDoesNotExist() verify.close() path = {'confname': self.name, 'filename': vc.filename} except ObjectDoesNotExist: # first time this tts request is made to this config fnamehash = '_'.join([self.name, sha1(text).hexdigest()]) storage = create_file(os.path.join('tts', self.name, fnamehash)) if storage: stat = self.driver.request_buffer_ex(text, fmt=self.encoding, volume=self.volume, speed=self.speed, pitch=self.pitch) if (stat['status'] == driver_h.TTS_RESULT_SUCCESS): vc = VoiceClip.objects.create( filename=fnamehash, spoken_text=text, checksum=sha1(''.join(map(chr, stat['data']))).hexdigest(), config=self) storage.set_contents(stat['data']) storage.close() path = {'confname': self.name, 'filename': vc.filename} else: logger.error("Driver request failed: %s" % stat['status_text']) else: # create_file/get_file api is lacking error handling, we should # return more than None if failure possibly preserving exceptions. # Saw permission issue once, we should pass that info up stack. stat = "Storage creation failure: %s" % fnamehash logger.error(stat) return (path, stat)
def get_encoding_string(self): """ Implement in sub-class, which does all the dirty work """ name = sys._getframe().f_code.co_name logger.error("%s not implemented in base-class" % name) raise NotImplementedError("%s needs to be implemented in sub-class" % name)
def get_or_create_voice_path(self, text): """ Implement in sub-class, which does all the dirty work """ name = sys._getframe().f_code.co_name logger.error("%s not implemented in base-class" % name) raise NotImplementedError("%s needs to be implemented in sub-class" % name)