1 # -*- coding: utf-8 -*-
2 #A part of NonVisual Desktop Access (NVDA)
3 # speech engine nvdajp_jtalk
4 # Copyright (C) 2010-2014 Takuya Nishimoto (nishimotz.com)
6 from logHandler import log
17 from .. import _espeak
18 from jtalkCore import *
20 from ..jtalk._nvdajp_unicode import unicode_normalize
21 from ..jtalk import _bgthread
25 from jtalkDir import jtalk_dir, dic_dir, user_dics
29 RATE_BOOST_MULTIPLIER = 1.5
31 # math.log(150) = 5.0, math.log(350) = 5.86
40 "speaker_attenuation": 1.0,
41 "htsvoice": os.path.join(jtalk_dir, 'm001', 'm001.htsvoice'),
42 "espeak_variant": "max"},
50 "speaker_attenuation": 0.5,
51 "htsvoice": os.path.join(jtalk_dir, 'mei', 'mei_normal.htsvoice'),
52 "espeak_variant": "f1"},
60 "speaker_attenuation": 1.0,
61 "htsvoice": os.path.join(jtalk_dir, 'lite', 'voice.htsvoice'),
62 "espeak_variant": "max"},
64 default_jtalk_voice = _jtalk_voices[1] # V2
67 class VoiceProperty(baseObject.AutoPropertyObject):
69 super(VoiceProperty,self).__init__()
71 # if samp_rate==16000: normal speed = 80samples period
78 speaker_attenuation = 1.0
85 currentEngine = 0 # 1:espeak 2:jtalk
88 return _bgthread.isSpeaking
91 _bgthread.isSpeaking = b
93 def _jtalk_speak(msg, index=None, prop=None):
94 global currIndex, buff
97 if prop is None: return
99 if prop.characterMode:
100 fperiod_current = voice_args['fperiod']
102 fperiod_current = fperiod
103 msg = unicode_normalize(msg)
104 msg = jtalkPrepare.convert(msg)
106 if DEBUG: lw = logwrite
109 if DEBUG: logwrite("p:%d i:%d msg:%s" % (prop.pitch, prop.inflection, msg))
110 level = int(max_level * speaker_attenuation)
111 la = 0.020 * prop.inflection # 50 = original range
112 ls = 0.015 * (prop.pitch - 50.0 + voice_args['pitch_bias']) # 50 = no shift
113 lo = ls + voice_args['lf0_base'] * (1 - la)
114 if DEBUG: logwrite("lo:%f la:%f" % (lo, la))
115 for t in string.split(msg):
116 if DEBUG: logwrite("unicode (%s)" % t)
118 if DEBUG: logwrite("utf-8 (%s)" % s.decode('utf-8', 'ignore'))
119 if not isSpeaking(): libjt_refresh(); return
121 Mecab_analysis(s, mf, logwrite_=logwrite)
122 if DEBUG: Mecab_print(mf, logwrite)
123 Mecab_correctFeatures(mf)
124 if DEBUG: Mecab_print(mf, logwrite)
125 ar = Mecab_splitFeatures(mf, CODE_='utf-8')
128 if DEBUG: Mecab_print(a, logwrite, CODE_='utf-8')
129 Mecab_utf8_to_cp932(a)
130 if DEBUG: logwrite("Mecab_analysis done")
134 fperiod_ = fperiod_current,
135 feed_func_ = player.feed, # player.feed() is called inside
136 is_speaking_func_ = isSpeaking,
137 begin_thres_ = thres_level,
138 end_thres_ = thres2_level,
144 if DEBUG: logwrite("libjt_synthesis done")
148 lastIndex = currIndex
155 def _espeak_speak(msg, lang, index=None, prop=None):
156 global currentEngine, lastIndex, espeakMark
159 msg.translate({ord(u'\01'):None,ord(u'<'):u'<',ord(u'>'):u'>'})
160 msg = u"<voice xml:lang=\"%s\">%s</voice>" % (lang, msg)
161 msg += u"<mark name=\"%d\" />" % espeakMark
163 while currentEngine == 1 and _espeak.lastIndex != espeakMark:
174 msg, lang, index, prop = arg
175 if DEBUG: logwrite('[' + lang + ']' + msg)
176 if DEBUG: logwrite("_speak(%s)" % msg)
178 _jtalk_speak(msg, index, prop)
180 _espeak_speak(msg, lang, index, prop)
183 def _updateSpeakIndex(index):
186 lastIndex = currIndex = index
188 def speak(msg, lang, index=None, voiceProperty_=None):
189 if msg is None and lang is None:
190 _bgthread.execWhenDone(_updateSpeakIndex, index, mustBeAsync=True)
193 if len(msg) == 0: return
194 if voiceProperty_ is None: return
195 arg = [msg, lang, index, copy.deepcopy(voiceProperty_)]
196 _bgthread.execWhenDone(_speak, arg, mustBeAsync=True)
200 if currentEngine == 1:
204 # Kill all speech from now.
205 # We still want parameter changes to occur, so requeue them.
207 stop_task_count = 0 # for log.info()
210 item = _bgthread.bgQueue.get_nowait() # [func, args, kwargs]
211 if item[0] != _speak:
214 stop_task_count = stop_task_count + 1
215 _bgthread.bgQueue.task_done()
217 # Let the exception break us out of this loop, as queue.empty() is not reliable anyway.
220 _bgthread.bgQueue.put(item)
222 if DEBUG: logwrite("stop: %d task(s) stopping" % stop_task_count)
227 if currentEngine == 1:
228 _espeak.pause(switch)
229 elif currentEngine == 2:
232 def initialize(voice = default_jtalk_voice):
233 global player, voice_args
234 global speaker_attenuation
236 speaker_attenuation = voice_args['speaker_attenuation']
237 if not _espeak.espeakDLL:
239 log.debug("jtalk using eSpeak version %s" % _espeak.info())
240 _espeak.setVoiceByLanguage("en")
241 _espeak.setVoiceAndVariant(variant=voice["espeak_variant"])
243 player = nvwave.WavePlayer(channels=1, samplesPerSec=voice_args['samp_rate'], bitsPerSample=16, outputDevice=config.conf["speech"]["outputDevice"])
244 if not _bgthread.bgThread:
245 _bgthread.initialize()
247 Mecab_initialize(log.info, jtalk_dir, dic_dir, user_dics)
250 jt_dll = os.path.join(jtalk_dir, 'libopenjtalk.dll')
251 log.debug('jt_dll %s' % jt_dll)
252 libjt_initialize(jt_dll)
253 log.debug(libjt_version())
255 if os.path.isfile(voice_args['htsvoice']):
256 libjt_load(voice_args['htsvoice'])
257 log.info("loaded " + voice_args['htsvoice'])
259 log.error("load error " + voice_args['htsvoice'])
264 _bgthread.terminate()
271 def get_rate(rateBoost):
274 def set_rate(rate, rateBoost):
275 global fperiod, rate_percent
277 if voice_args['samp_rate'] == 16000:
278 fperiod = int(80 - int(rate) / 2) # 80..30
279 if voice_args['samp_rate'] == 48000:
280 fperiod = int(240 - 1.5 * int(rate)) # 240..90
282 fperiod = int(fperiod * RATE_BOOST_MULTIPLIER)
285 global max_level, thres_level, thres2_level
286 max_level = int(326.67 * int(vol) + 100) # 100..32767