Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor mimic_tts to not load config when importing #2888

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 43 additions & 21 deletions mycroft/tts/mimic_tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,43 @@

from .tts import TTS, TTSValidator

CONFIG = Configuration.get().get("tts").get("mimic")
DATA_DIR = expanduser(Configuration.get()['data_dir'])

BIN = CONFIG.get("path",
os.path.join(MYCROFT_ROOT_PATH, 'mimic', 'bin', 'mimic'))
def get_mimic_binary():
"""Find the mimic binary, either from config or from PATH.

if not os.path.isfile(BIN):
# Search for mimic on the path
import distutils.spawn
Returns:
(str) path of mimic executable
"""
config = Configuration.get().get("tts", {}).get("mimic")

bin_ = config.get("path",
os.path.join(MYCROFT_ROOT_PATH, 'mimic', 'bin', 'mimic'))

if not os.path.isfile(bin_):
# Search for mimic on the path
import distutils.spawn

bin_ = distutils.spawn.find_executable("mimic")

BIN = distutils.spawn.find_executable("mimic")
return bin_

SUBSCRIBER_VOICES = {'trinity': join(DATA_DIR, 'voices/mimic_tn')}

def get_subscriber_voices():
"""Get dict of mimic voices exclusive to subscribers.

Returns:
(dict) map of voices to custom Mimic executables.
"""
data_dir = expanduser(Configuration.get()['data_dir'])
return {'trinity': join(data_dir, 'voices/mimic_tn')}


def download_subscriber_voices(selected_voice):
"""Function to download all premium voices.

The function starts with the currently selected if applicable
"""
subscriber_voices = get_subscriber_voices()

def make_executable(dest):
"""Call back function to make the downloaded file executable."""
Expand All @@ -61,7 +78,7 @@ def make_executable(dest):
os.chmod(dest, file_stat.st_mode | stat.S_IEXEC)

# First download the selected voice if needed
voice_file = SUBSCRIBER_VOICES.get(selected_voice)
voice_file = subscriber_voices.get(selected_voice)
if voice_file is not None and not exists(voice_file):
LOG.info('Voice doesn\'t exist, downloading')
url = DeviceApi().get_subscriber_voice_url(selected_voice)
Expand All @@ -76,8 +93,8 @@ def make_executable(dest):
.format(selected_voice))

# Download the rest of the subsciber voices as needed
for voice in SUBSCRIBER_VOICES:
voice_file = SUBSCRIBER_VOICES[voice]
for voice in subscriber_voices:
voice_file = subscriber_voices[voice]
if not exists(voice_file):
url = DeviceApi().get_subscriber_voice_url(voice)
# Check we got an url
Expand Down Expand Up @@ -111,9 +128,12 @@ def __init__(self, lang, config):
lang, config, MimicValidator(self), 'wav',
ssml_tags=["speak", "ssml", "phoneme", "voice", "audio", "prosody"]
)
self.default_binary = get_mimic_binary()

self.clear_cache()

# Download subscriber voices if needed
self.subscriber_voices = get_subscriber_voices()
self.is_subscriber = DeviceApi().is_subscriber
if self.is_subscriber:
trd = Thread(target=download_subscriber_voices, args=[self.voice])
Expand All @@ -137,18 +157,19 @@ def modify_tag(self, tag):
@property
def args(self):
"""Build mimic arguments."""
if (self.voice in SUBSCRIBER_VOICES and
exists(SUBSCRIBER_VOICES[self.voice]) and self.is_subscriber):
subscriber_voices = self.subscriber_voices
if (self.voice in subscriber_voices and
exists(subscriber_voices[self.voice]) and self.is_subscriber):
# Use subscriber voice
mimic_bin = SUBSCRIBER_VOICES[self.voice]
mimic_bin = subscriber_voices[self.voice]
voice = self.voice
elif self.voice in SUBSCRIBER_VOICES:
elif self.voice in subscriber_voices:
# Premium voice but bin doesn't exist, use ap while downloading
mimic_bin = BIN
mimic_bin = self.default_binary
voice = 'ap'
else:
# Normal case use normal binary and selected voice
mimic_bin = BIN
mimic_bin = self.default_binary
voice = self.voice

args = [mimic_bin, '-voice', voice, '-psdur', '-ssml']
Expand Down Expand Up @@ -195,11 +216,12 @@ def validate_lang(self):

def validate_connection(self):
"""Check that Mimic executable is found and works."""
mimic_bin = get_mimic_binary()
try:
subprocess.call([BIN, '--version'])
subprocess.call([mimic_bin, '--version'])
except Exception as err:
if BIN:
LOG.error('Failed to find mimic at: {}'.format(BIN))
if mimic_bin:
LOG.error('Failed to find mimic at: {}'.format(mimic_bin))
else:
LOG.error('Mimic executable not found')
raise Exception(
Expand Down
13 changes: 8 additions & 5 deletions test/unittests/tts/test_mimic_tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import unittest
from unittest import mock

from mycroft.tts.mimic_tts import (Mimic, download_subscriber_voices, BIN,
SUBSCRIBER_VOICES)
from mycroft.tts.mimic_tts import (Mimic, download_subscriber_voices,
get_mimic_binary,
get_subscriber_voices)


device_instance_mock = mock.Mock(name='device_api_instance')
Expand Down Expand Up @@ -55,15 +56,17 @@ def test_viseme(self, _, mock_device_api):
@mock.patch('mycroft.tts.mimic_tts.Thread')
def test_subscriber(self, mock_thread, _, mock_device_api):
mock_device_api.return_value = subscribed_device

default_mimic = get_mimic_binary()
trinity_mimic = get_subscriber_voices()['trinity']
m = Mimic('en-US', {'voice': 'trinity'})
mock_thread.assert_called_with(target=download_subscriber_voices,
args=['trinity'])
self.assertTrue(m.is_subscriber)
self.assertEqual(m.args, [BIN, '-voice', 'ap', '-psdur', '-ssml'])
self.assertEqual(m.args,
[default_mimic, '-voice', 'ap', '-psdur', '-ssml'])
with mock.patch('mycroft.tts.mimic_tts.exists') as mock_exists:
mock_exists.return_value = True
self.assertEqual(m.args, [SUBSCRIBER_VOICES['trinity'], '-voice',
self.assertEqual(m.args, [trinity_mimic, '-voice',
'trinity', '-psdur', '-ssml'])

@mock.patch('mycroft.tts.mimic_tts.sleep')
Expand Down