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

Commit

Permalink
Merge 5e965d4 into b712b0e
Browse files Browse the repository at this point in the history
  • Loading branch information
forslund authored Apr 28, 2021
2 parents b712b0e + 5e965d4 commit f3246b4
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 f3246b4

Please sign in to comment.