~mshinke/nvdajp/betterJtalk

« back to all changes in this revision

Viewing changes to source/synthDrivers/_nvdajp_jtalk.py

refactored _bgthread and _nvdajp_jtalk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# _nvdajp_jtalk.py 
2
2
# -*- coding: utf-8 -*-
3
3
#A part of NonVisual Desktop Access (NVDA)
4
 
#Copyright (C) 2006-2010 NVDA Contributors <http://www.nvda-project.org/>
5
 
#This file is covered by the GNU General Public License.
6
 
#See the file COPYING for more details.
7
 
 
8
4
# speech engine nvdajp_jtalk
9
 
# Copyright (C) 2010-2011 Takuya Nishimoto (nishimotz.com)
 
5
# Copyright (C) 2010-2012 Takuya Nishimoto (nishimotz.com)
10
6
# based on Open JTalk (bin/open_jtalk.c) http://github.com/nishimotz/libopenjtalk/
11
 
# based on NVDA (synthDrivers/_espeak.py)
12
7
 
13
8
from logHandler import log
14
9
import time
15
 
import threading
16
10
import Queue
17
11
import os
18
12
import codecs
22
16
import nvwave
23
17
import _jtalk_core
24
18
import _nvdajp_predic 
 
19
import _bgthread
 
20
 
 
21
DEBUG_INFO = None
 
22
MSGLEN = 1000
 
23
RATE_BOOST_MULTIPLIER = 1.5
25
24
 
26
25
JT_DLL = r"synthDrivers\jtalk\libopenjtalk.dll"
27
26
 
46
45
thres2_level = 128
47
46
speaker_attenuation = 1.0
48
47
 
49
 
DEBUG_INFO = None
50
 
 
51
48
logwrite = None
52
49
lastIndex = None
53
50
currIndex = None
54
 
 
55
 
###########################################
56
 
 
57
 
def is_speaking_func():
58
 
        return isSpeaking
59
 
 
60
 
############################################
61
 
# based on _espeak.py (nvda)
62
 
 
63
 
isSpeaking = False
64
51
lastIndex = None
65
 
bgThread = None
66
 
bgQueue = None
67
52
player = None
68
 
 
69
 
class BgThread(threading.Thread):
70
 
        def __init__(self):
71
 
                threading.Thread.__init__(self)
72
 
                self.setDaemon(True)
73
 
 
74
 
        def run(self):
75
 
                global isSpeaking
76
 
                while True:
77
 
                        func, args, kwargs = bgQueue.get()
78
 
                        if not func:
79
 
                                break
80
 
                        try:
81
 
                                func(*args, **kwargs)
82
 
                        except:
83
 
                                log.error("Error running function from queue", exc_info=True)
84
 
                        bgQueue.task_done()
85
 
 
86
 
def _execWhenDone(func, *args, **kwargs):
87
 
        global bgQueue
88
 
        # This can't be a kwarg in the function definition because it will consume the first non-keywor dargument which is meant for func.
89
 
        mustBeAsync = kwargs.pop("mustBeAsync", False)
90
 
        if mustBeAsync or bgQueue.unfinished_tasks != 0:
91
 
                # Either this operation must be asynchronous or There is still an operation in progress.
92
 
                # Therefore, run this asynchronously in the background thread.
93
 
                bgQueue.put((func, args, kwargs))
94
 
        else:
95
 
                func(*args, **kwargs)
96
 
 
97
 
MSGLEN = 1000
98
 
buff = ctypes.create_string_buffer(MSGLEN)
 
53
buff = None
 
54
 
 
55
def isSpeaking():
 
56
        return _bgthread.isSpeaking
 
57
 
 
58
def setSpeaking(b):
 
59
        _bgthread.isSpeaking = b
99
60
 
100
61
# call from BgThread
101
62
def _speak(msg, index=None):
102
63
        global currIndex, buff
103
64
        currIndex = index
104
 
        global isSpeaking
105
 
        isSpeaking = True
 
65
        setSpeaking(True)
106
66
        msg = _nvdajp_predic.convert(msg)
107
67
        if DEBUG_INFO: logwrite("_speak(%s)" % msg)
108
68
        lw = None
109
69
        if DEBUG_INFO: lw = logwrite
110
70
        for m in string.split(msg):
111
 
                if len(m) > 0:
112
 
                        try:
113
 
                                if DEBUG_INFO: logwrite("processing (%s)" % m)
114
 
                                text = m.encode(_jtalk_core.CODE, 'ignore')
115
 
                                _jtalk_core.Mecab_text2mecab(buff, text); str = buff.value
116
 
                                if DEBUG_INFO: logwrite("libjt_text2mecab done")
117
 
                                if not isSpeaking: _jtalk_core.libjt_refresh(); return
118
 
                                mf = _jtalk_core.MecabFeatures()
119
 
                                _jtalk_core.Mecab_analysis(str, mf)
120
 
                                if DEBUG_INFO: logwrite("Mecab_analysis done")
121
 
                                if not isSpeaking: _jtalk_core.libjt_refresh(); return
122
 
                                _jtalk_core.libjt_synthesis(mf.feature, mf.size, 
123
 
                                        fperiod_ = fperiod, 
124
 
                                        feed_func_ = player.feed, # player.feed() is called inside
125
 
                                        is_speaking_func_ = is_speaking_func, 
126
 
                                        thres_ = thres_level,
127
 
                                        thres2_ = thres2_level,
128
 
                                        level_ = int(max_level * speaker_attenuation),
129
 
                                        logwrite_ = lw,
130
 
                                        lf0_offset_ = lf0_offset,
131
 
                                        lf0_amp_ = lf0_amp)
132
 
                                mf = None
133
 
                                if DEBUG_INFO: logwrite("libjt_synthesis done")
134
 
                                _jtalk_core.libjt_refresh()
135
 
                        except WindowsError:
136
 
                                if DEBUG_INFO: logwrite("WindowsError")
 
71
                if len(m) == 0: continue
 
72
                try:
 
73
                        if DEBUG_INFO: logwrite("processing (%s)" % m)
 
74
                        text = m.encode(_jtalk_core.CODE, 'ignore')
 
75
                        _jtalk_core.Mecab_text2mecab(buff, text); str = buff.value
 
76
                        if DEBUG_INFO: logwrite("libjt_text2mecab done")
 
77
                        if not isSpeaking(): _jtalk_core.libjt_refresh(); return
 
78
                        mf = _jtalk_core.MecabFeatures()
 
79
                        _jtalk_core.Mecab_analysis(str, mf)
 
80
                        if DEBUG_INFO: logwrite("Mecab_analysis done")
 
81
                        if not isSpeaking(): _jtalk_core.libjt_refresh(); return
 
82
                        _jtalk_core.libjt_synthesis(mf.feature, mf.size, 
 
83
                                fperiod_ = fperiod, 
 
84
                                feed_func_ = player.feed, # player.feed() is called inside
 
85
                                is_speaking_func_ = isSpeaking, 
 
86
                                thres_ = thres_level,
 
87
                                thres2_ = thres2_level,
 
88
                                level_ = int(max_level * speaker_attenuation),
 
89
                                logwrite_ = lw,
 
90
                                lf0_offset_ = lf0_offset,
 
91
                                lf0_amp_ = lf0_amp)
 
92
                        mf = None
 
93
                        if DEBUG_INFO: logwrite("libjt_synthesis done")
 
94
                        _jtalk_core.libjt_refresh()
 
95
                except WindowsError:
 
96
                        if DEBUG_INFO: logwrite("WindowsError")
137
97
        global lastIndex
138
98
        lastIndex = currIndex
139
99
        currIndex = None
140
 
        isSpeaking = False
 
100
        setSpeaking(False)
141
101
 
142
102
def speak(msg, index=None):
143
103
        msg = msg.rstrip()
144
104
        if msg == '': return
145
105
        if msg == u'ー': msg = u'チョーオン'
146
106
        if msg == u'ン': msg = u'ウン'
147
 
        _execWhenDone(_speak, msg, index, mustBeAsync=True)
 
107
        _bgthread.execWhenDone(_speak, msg, index, mustBeAsync=True)
148
108
 
149
109
def stop():
150
 
        global isSpeaking, bgQueue
151
110
        # Kill all speech from now.
152
111
        # We still want parameter changes to occur, so requeue them.
153
112
        params = []
154
113
        stop_task_count = 0 # for log.info()
155
114
        try:
156
115
                while True:
157
 
                        item = bgQueue.get_nowait() # [func, args, kwargs]
 
116
                        item = _bgthread.bgQueue.get_nowait() # [func, args, kwargs]
158
117
                        if item[0] != _speak:
159
118
                                params.append(item)
160
119
                        else:
161
120
                                stop_task_count = stop_task_count + 1
162
 
                        bgQueue.task_done()
 
121
                        _bgthread.bgQueue.task_done()
163
122
        except Queue.Empty:
164
123
                # Let the exception break us out of this loop, as queue.empty() is not reliable anyway.
165
124
                pass
166
125
        for item in params:
167
 
                bgQueue.put(item)
168
 
        isSpeaking = False
 
126
                _bgthread.bgQueue.put(item)
 
127
        setSpeaking(False)
169
128
        if DEBUG_INFO: logwrite("stop: %d task(s) stopping" % stop_task_count)
170
129
        player.stop()
171
130
        lastIndex = None
175
134
        player.pause(switch)
176
135
 
177
136
def initialize(voice = _jtalk_voices[0]):
178
 
        global bgThread, bgQueue, player, logwrite, voice_args
 
137
        global player, logwrite, voice_args
179
138
        global speaker_attenuation
 
139
        global buff
 
140
        buff = ctypes.create_string_buffer(MSGLEN)
180
141
        voice_args = voice
181
142
        speaker_attenuation = voice_args['speaker_attenuation']
182
143
        player = nvwave.WavePlayer(channels=1, samplesPerSec=voice_args['samp_rate'], bitsPerSample=16)
183
 
        bgQueue = Queue.Queue()
184
 
        bgThread = BgThread()
185
 
        bgThread.start()
 
144
        _bgthread.initialize()
186
145
        #
187
146
        _jtalk_core.libjt_initialize(JT_DLL, **voice_args)
188
147
        _jtalk_core.libjt_load(voice_args['dir'])
196
155
        if DEBUG_INFO: logwrite("jtalk for NVDA started. voice:" + voice_args['dir'])
197
156
 
198
157
def terminate():
199
 
        global bgThread, bgQueue, player
 
158
        global player
200
159
        stop()
201
 
        bgQueue.put((None, None, None))
202
 
        bgThread.join()
203
 
        bgThread = None
204
 
        bgQueue = None
 
160
        _bgthread.terminate()
205
161
        player.close()
206
162
        player = None
207
163
 
208
 
RATE_BOOST_MULTIPLIER = 1.5
209
 
 
210
164
def get_rate(rateBoost):
211
165
        f = fperiod
212
166
        if not rateBoost: