Skip to content
This repository has been archived by the owner on Sep 8, 2024. It is now read-only.

Commit

Permalink
Refactor mimic_tts to not load config when importing
Browse files Browse the repository at this point in the history
Hitting the entire configuration fetching routine with call to the
backend is not polite to do when just importing the file. This moves the
config lookup out of the global scope and into special functions for
finding the mimic binary and looking up the data path for the subscriber
voices.
  • Loading branch information
forslund committed Apr 28, 2021
1 parent b712b0e commit 5e965d4
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 26 deletions.
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 for 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

0 comments on commit 5e965d4

Please sign in to comment.