Ejemplo n.º 1
0
def set_profile_password(path, value):
    global _profile
    _logger = logging.getLogger(__name__)
    if (isinstance(path, str)):
        path = [path]
    # Encrypt value
    first_id = hashlib.sha256(
        run_command("cat /etc/machine-id".split(),
                    capture=1).stdout).hexdigest()
    second_id = hashlib.sha256(
        run_command("hostid".split(), capture=1).stdout).hexdigest()
    try:
        third_idb1 = run_command("blkid".split(),
                                 capture=1).stdout.decode().strip()
        third_id = hashlib.sha256(
            run_command("""grep -oP 'UUID="\\K[^"]+'""".split(),
                        capture=4,
                        stdin=third_idb1).stdout).hexdigest()
    except FileNotFoundError:
        _logger.warning(" ".join([
            "Package not installed: 'blkid'",
            "Please install it manually or run apt_requirements.sh again"
        ]))
        third_id = ""
    salt = get_profile_key()
    kdf = PBKDF2HMAC(algorithm=hashes.SHA512(),
                     length=32,
                     salt=salt,
                     iterations=100000,
                     backend=default_backend())
    password = ''.join([first_id, second_id, third_id]).encode()
    key = base64.urlsafe_b64encode(kdf.derive(password))
    cipher_suite = Fernet(key)
    cipher_text = cipher_suite.encrypt(value.encode("utf-8")).decode("utf-8")
    set_profile_var(path, cipher_text)
Ejemplo n.º 2
0
def generate_scorer(directory, phrases):
    print("Generating scorer: {}".format(directory))
    languagemodel_path = get_languagemodel_path(directory)
    with open(languagemodel_path, "w") as f:
        f.writelines(line_generator(phrases))
    version = "v{}".format(deepspeech.version())
    sourcedir_name = "STT{}".format(version)
    sourcedir_path = paths.sub(os.path.join("sources", sourcedir_name))
    kenlm_sourcedir = paths.sub(os.path.join("sources", "kenlm"))
    kenlm_bins = os.path.join(kenlm_sourcedir, 'build', 'bin')
    cmd = [
        'python',
        os.path.join(sourcedir_path, 'data', 'lm',
                     'generate_lm.py'), '--input_txt', languagemodel_path,
        '--output_dir', directory, '--kenlm_bins', kenlm_bins, '--binary_type',
        'trie', '--top_k', '500000', '--arpa_order', '5', '--max_arpa_memory',
        '10%', '--arpa_prune', '0|0|1', '--binary_a_bits', '255',
        '--binary_q_bits', '8', '--discount_fallback'
    ]
    completed_process = run_command(cmd, 2)
    if (completed_process.returncode == 0):
        # There should be an additional step here where the
        # default values of alpha and beta are generated by
        # lm_optimizer. However, that involves building a
        # dev set from data in the audiolog.
        # Generate the scorer package
        # I'm still trying to figure out where to get the
        # generate_scorer_package binary. I found mine in the
        # ~/.cache/bazel folder after running bazel build
        # on //native_client:generate_scorer_package
        # I think there are binary packages available on
        # github, but you have to know your processor type,
        # and since I am testing on Raspbianx86, I have to
        # build it. x86_64 or rpi are available on Github.
        print("Generating scorer package")
        binarydir_name = "native_client{}".format(version)
        binarydir_path = paths.sub(
            os.path.join("sources", binarydir_name, "generate_scorer_package"))
        cmd = [
            binarydir_path, '--alphabet',
            os.path.join(sourcedir_path, 'data', 'alphabet.txt'), '--lm',
            os.path.join(directory, 'lm.binary'), '--vocab',
            os.path.join(directory, 'vocab-500000.txt'), '--package',
            os.path.join(directory, 'scorer'), '--default_alpha',
            '0.931289105002', '--default_beta', '1.18341375810284'
        ]
        completed_process = run_command(cmd, 2)
        if (completed_process.returncode != 0):
            print(completed_process.stderr.decode("UTF-8"))
    else:
        print(completed_process.stderr.decode("UTF-8"))
    print(os.path.join(directory, 'scorer'))
    return os.path.join(directory, 'scorer')
Ejemplo n.º 3
0
def check_pocketsphinx_model(directory):
    # Start by assuming the files exist. If any file is found to not
    # exist, then set this to False
    FilesExist = True
    if (not os.path.isfile(os.path.join(directory, "mdef.txt"))):
        if (os.path.isfile(os.path.join(directory, "mdef"))):
            command = [
                "pocketsphinx_mdef_convert", "-text",
                os.path.join(directory, "mdef"),
                os.path.join(directory, "mdef.txt")
            ]
            completedprocess = run_command(command)
            print("Command {} returned {}".format(
                " ".join(completedprocess.args), completedprocess.returncode))
        if (not os.path.isfile(os.path.join(directory, "mdef.txt"))):
            FilesExist = False
    if (not os.path.isfile(os.path.join(directory, "means"))):
        FilesExist = False
    if (not os.path.isfile(os.path.join(directory, "mixture_weights"))):
        FilesExist = False
    if (not os.path.isfile(os.path.join(directory, "sendump"))):
        FilesExist = False
    if (not os.path.isfile(os.path.join(directory, "variances"))):
        FilesExist = False
    return FilesExist
Ejemplo n.º 4
0
 def say(self, phrase):
     cmd = [
         'espeak', '-v', self.voice, '-p', self.pitch_adjustment, '-s',
         self.words_per_minute, '--stdout', phrase
     ]
     cmd = [str(x) for x in cmd]
     self._logger.debug('Executing %s',
                        ' '.join([pipes.quote(arg) for arg in cmd]))
     data = run_command(cmd, 1).stdout
     return data
Ejemplo n.º 5
0
 def get_voices(self):
     output = run_command(['espeak', '--voices'], 1).stdout.decode("UTF-8")
     output += run_command(['espeak', '--voices=mbrola'],
                           1).stdout.decode("UTF-8")
     voices = []
     for pty, lang, gender, name, other in RE_PATTERN.findall(output):
         voices.append(
             Voice(name=name,
                   gender=gender,
                   priority=int(pty),
                   language=lang))
         if len(other) > 0:
             for lang2, pty2 in RE_OTHER.findall(other):
                 voices.append(
                     Voice(name=name,
                           gender=gender,
                           priority=int(pty2),
                           language=lang2))
     return sorted(voices, key=lambda voice: voice.priority)
Ejemplo n.º 6
0
    def settings(self):
        _ = self.gettext
        try:
            tz = run_command(["/bin/cat", "/etc/timezone"],
                             capture=1).stdout.decode('utf-8').strip()
        except OSError:
            tz = None

        return OrderedDict({
            ('timezone', ): {
                "title":
                _("What is your timezone?"),
                "description":
                _("Please enter a timezone from the list located in the TZ* column at http://en.wikipedia.org/wiki/List_of_tz_database_time_zones"
                  ),
                "default":
                tz
            }
        })
Ejemplo n.º 7
0
    def __init__(self, *args, **kwargs):
        """
        Create Plugin Instance
        """
        plugin.STTPlugin.__init__(self, *args, **kwargs)

        # Check that we have the correct project source downloaded
        # Currently we are only using this for the generate_lm.py
        # script.
        version = "v{}".format(deepspeech.version())
        sourcedir_name = "STT{}".format(version)
        sourcedir_path = paths.sub(os.path.join("sources", sourcedir_name))
        if (not os.path.isdir(sourcedir_path)):
            # use git to download the appropriate source directory
            print(
                "Downloading (cloning) Mozilla DeepSpeech Source to {}".format(
                    sourcedir_path))
            cmd = [
                'git', 'clone', '-b', version,
                'https://github.com/mozilla/STT', sourcedir_path
            ]
            completed_process = run_command(cmd, 2)
            if (completed_process.returncode != 0):
                self._logger.error(completed_process.stderr.decode("UTF-8"))
                exit(1)
        # Download the release binaries. We need to get the
        # generate_scorer_package from here.
        binarydir_name = "native_client{}".format(version)
        binarydir_path = paths.sub(os.path.join("sources", binarydir_name))
        if (not os.path.isdir(binarydir_path)):
            os.makedirs(binarydir_path)
            binary_url = None
            if (platform.machine() == "x86_64"):
                arch = "amd64"
                binary_url = 'https://github.com/mozilla/DeepSpeech/releases/download/{}/native_client.{}.cpu.linux.tar.xz'.format(
                    version, arch)
            if (platform.machine() == "armv7l"):
                arch = "rpi3"
                binary_url = 'https://github.com/mozilla/DeepSpeech/releases/download/{}/native_client.{}.cpu.linux.tar.xz'.format(
                    version, arch)
            # Unfortunately, it does not appear that the native client is
            # compiled for x86 machines, so Raspbianx86 users are out of luck
            # for right now.
            if (binary_url is None):
                print(
                    "Architecture not recognized. Please add it to {}".format(
                        __file__))
            else:
                print("Downloading native client binaries from {}".format(
                    binary_url))
                cmd = [
                    'wget', binary_url,
                    '--directory-prefix={}'.format(binarydir_path),
                    '--output-document={}'.format(
                        os.path.join(binarydir_path, 'native_client.tar.xz'))
                ]
                print(" ".join(cmd))
                completed_process = run_command(cmd, 2)
                if (completed_process.returncode == 0):
                    # unzip the archive into the binarydir_path directory
                    cmd = [
                        'tar', 'Jxvf',
                        os.path.join(binarydir_path, 'native_client.tar.xz'),
                        '-C', binarydir_path
                    ]
                    completed_process = run_command(cmd, 2)
                    if (completed_process.returncode != 0):
                        print(completed_process.stderr.decode("UTF-8"))
                else:
                    print(completed_process.stderr.decode("UTF-8"))
        kenlm_sourcedir = paths.sub(os.path.join("sources", "kenlm"))
        if (not os.path.isdir(kenlm_sourcedir)):
            # use git to download kenlm
            print("Cloning KenLM")
            cmd = [
                'git', 'clone', 'https://github.com/kpu/kenlm.git',
                kenlm_sourcedir
            ]
            completed_process = run_command(cmd, 2)
            if (completed_process.returncode == 0):
                # build kenlm
                print("Building KenLM")
                build_dir = os.path.join(kenlm_sourcedir, "build")
                if (not os.path.isdir(build_dir)):
                    os.makedirs(build_dir)
                    os.chdir(build_dir)
                    cmd = ['cmake', '..']
                    completed_process = run_command(cmd, 2, cwd=build_dir)
                    if (completed_process.returncode == 0):
                        cmd = ['make']
                        completed_process = run_command(cmd, 2, cwd=build_dir)
                        if (completed_process.returncode != 0):
                            self._logger.error(
                                completed_process.stderr.decode("UTF-8"))
                            exit(1)
                    else:
                        self._logger.error(
                            completed_process.stderr.decode("UTF-8"))
                        exit(1)
            else:
                self._logger.error(completed_process.stderr.decode("UTF-8"))
                exit(1)

        # Beam width used in the CTC decoder when building candidate
        # transcriptions
        self._BEAM_WIDTH = profile.get(['deepspeech', 'beam_width'], 500)

        # Only 16KHz files are currently supported
        self._FS = profile.get(['deepspeech', 'fs'], 16000)

        # These are paths. They are required.
        # Path to the model (protocol buffer binary file)
        working_dir = os.path.expanduser(
            profile.get(['deepspeech', 'working_dir']))
        if (not os.path.isdir(working_dir)):
            os.makedirs(working_dir)
        download_url = 'https://github.com/mozilla/DeepSpeech/releases/download/{}/deepspeech-{}-models.pbmm'.format(
            version, deepspeech.version())
        self._MODEL = os.path.join(working_dir,
                                   "model_{}.pbmm".format(version))
        if (platform.machine() == 'armv7l'):
            download_url = 'https://github.com/mozilla/DeepSpeech/releases/download/{}/deepspeech-{}-models.tflite'.format(
                version, deepspeech.version())
            self._MODEL = os.path.join(working_dir,
                                       "model_{}.tflite".format(version))
        self._logger.info("Model: {}".format(self._MODEL))
        if (not os.path.isfile(self._MODEL)):
            print("Downloading {}".format(download_url))
            # FIXME it would be good to have a progress indicator here.
            # This can take a long time depending on your bandwidth.
            self._MODEL = app_utils.download_file(download_url, self._MODEL)
            print("Saved as {}".format(self._MODEL))
            print("Download completed")
        self._ds = deepspeech.Model(self._MODEL)
        scorer_file = os.path.join(self.compile_vocabulary(generate_scorer),
                                   "scorer")
        self._ds.enableExternalScorer(scorer_file)
Ejemplo n.º 8
0
def get_profile_password(path, default=None):
    """
    Get a value from the profile, whether it exists or not
    If the value does not exist in the profile, returns
    either the default value (if there is one) or None.
    """
    _logger = logging.getLogger(__name__)
    allowed = []
    allowed.append(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     'app_utils.py'))
    allowed.append(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     'commandline.py'))
    allowed.append(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     'application.py'))
    filename = inspect.getframeinfo(sys._getframe(1))[0]
    if (filename in allowed):
        if (isinstance(path, str)):
            path = [path]
        first_id = hashlib.sha256(
            run_command("cat /etc/machine-id".split(),
                        capture=1).stdout).hexdigest()
        second_id = hashlib.sha256(
            run_command("hostid".split(), capture=1).stdout).hexdigest()
        try:
            third_idb1 = run_command("blkid".split(),
                                     capture=1).stdout.decode().strip()
            third_id = hashlib.sha256(
                run_command("""grep -oP 'UUID="\\K[^"]+'""".split(),
                            capture=4,
                            stdin=third_idb1).stdout).hexdigest()
        except FileNotFoundError:
            _logger.warning(" ".join([
                "Package not installed: 'blkid'",
                "Please install it manually or run apt_requirements.sh again"
            ]))
            third_id = ""
        salt = get_profile_key()
        kdf = PBKDF2HMAC(algorithm=hashes.SHA512(),
                         length=32,
                         salt=salt,
                         iterations=100000,
                         backend=default_backend())
        password = ''.join([first_id, second_id, third_id]).encode()
        key = base64.urlsafe_b64encode(kdf.derive(password))
        cipher_suite = Fernet(key)
        response = get_profile_var(path, None)
        try:
            if (hasattr(response, "encode")):
                response = cipher_suite.decrypt(
                    response.encode("utf-8")).decode("utf-8")
        except InvalidToken:
            response = None
        if response is None:
            response = default
    else:
        print(
            "Access to encrypted profile elements not allowed from {}".format(
                filename))
        _logger.warn(
            "Access to encrypted profile elements not allowed from {}".format(
                filename))
        response = None
    return response
Ejemplo n.º 9
0
    def HandleCommand(self, command, description):
        try:
            conn = sqlite3.connect(self.audiolog_db)
            c = conn.cursor()
            response = []
            continue_next = True
            nextcommand = ""
            if (command == ""):
                response.append(
                    "<h2>Preparing to adapt Pocketsphinx model</h2>")
                description.append(
                    "Adapting standard {} pocketsphinx model".format(
                        self.language))
                nextcommand = "checkenviron"
            if (command == "checkenviron"):
                # Now run through the steps to adapt the standard model
                # Start by checking to see if we have a copy of the standard
                # model for this user's chosen language and download it if not.
                # Check for the files we need
                if (not check_pocketsphinx_model(self.standard_dir)):
                    # Check and see if we already have a copy of the standard
                    # language model
                    cmd = [
                        'git', 'clone', '-b', self.language,
                        'https://github.com/NaomiProject/CMUSphinx_standard_language_models.git',
                        self.standard_dir
                    ]
                    completedprocess = run_command(cmd)
                    response.append(
                        process_completedprocess(completedprocess,
                                                 output='html'))
                    if (completedprocess.returncode != 0):
                        continue_next = False
                response.append("Environment configured")
                nextcommand = "prepareworkingdir"
            if (command == "prepareworkingdir"):
                # At this point, we should have the standard model we need
                if (check_pocketsphinx_model(self.standard_dir)):
                    # FIXME It might be safest to remove the working dir at this
                    # point if it already exists
                    if not os.path.isdir(self.model_dir):
                        # Copy the sphinx model into model_dir
                        shutil.copytree(self.standard_dir, self.model_dir)
                    if (check_pocketsphinx_model(self.model_dir)):
                        query = " ".join([
                            "select", " rowid,", " case",
                            "  when length(trim(verified_transcription))>0",
                            "   then (length(trim(verified_transcription))-length(replace(trim(verified_transcription),' ','')))+1",
                            "  else 0", " end as WordCount,", " filename,",
                            " upper(trim(replace(replace(verified_transcription,'?',''),',',''))) as transcription",
                            "from audiolog",
                            "where type in('active','passive') and reviewed!=''"
                        ])
                        df = pd.read_sql_query(query, conn)
                        # Take the above and create naomi.fileids and naomi.transcription
                        # fileids:
                        description.append("on {} wav files".format(
                            str(df.shape[0])))
                        response.append("Adapting on {} wav files".format(
                            df.shape[0]))
                        with open(
                                os.path.join(self.working_dir,
                                             "naomi.fileids"), "w+") as f:
                            for filename in df['filename']:
                                # No need to copy file, just leave it in audiolog
                                f.write("{}\n".format(
                                    filename.rsplit(".", 1)[0]))
                        with open(
                                os.path.join(self.working_dir,
                                             "naomi.transcription"),
                                "w+") as f:
                            for t in df['transcription']:
                                f.write("<s> {} </s>\n".format(t.lower()))
                        nextcommand = "featureextraction"
                    else:
                        response.append(
                            "Error: failed to populate working model")
            if (command == "featureextraction"):
                cmd = [
                    'sphinx_fe', '-argfile',
                    os.path.join(self.model_dir,
                                 'feat.params'), '-samprate', '16000', '-c',
                    os.path.join(self.working_dir, 'naomi.fileids'), '-di',
                    self.audiolog_dir, '-do', self.working_dir, '-ei', 'wav',
                    '-eo', 'mfc', '-mswav', 'yes'
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode != 0):
                    continue_next = False
                nextcommand = "buildweights"
            if (command == "buildweights"):
                bw = '/usr/lib/sphinxtrain/bw'
                if os.path.isfile('/usr/local/libexec/sphinxtrain/bw'):
                    bw = '/usr/local/libexec/sphinxtrain/bw'
                cmd = [
                    bw, '-hmmdir', self.model_dir, '-moddeffn',
                    os.path.join(self.model_dir,
                                 'mdef.txt'), '-ts2cbfn', '.ptm.', '-feat',
                    '1s_c_d_dd', '-svspec', '0-12/13-25/26-38', '-cmn',
                    'current', '-agc', 'none', '-dictfn',
                    os.path.join(self.model_dir, 'cmudict.dict'), '-ctlfn',
                    os.path.join(self.working_dir, 'naomi.fileids'), '-lsnfn',
                    os.path.join(self.working_dir, 'naomi.transcription'),
                    '-cepdir', self.working_dir, '-accumdir', self.working_dir
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode != 0):
                    continue_next = False
                nextcommand = "mllr"
            if (command == "mllr"):
                # MLLR is a cheap adaptation method that is suitable when the amount of data is limited. It's good for online adaptation.
                # MLLR works best for a continuous model. It's effect for semi-continuous models is limited.
                mllr = '/usr/lib/sphinxtrain/mllr_solve'
                if os.path.isfile('/usr/local/libexec/sphinxtrain/mllr_solve'):
                    mllr = '/usr/local/libexec/sphinxtrain/mllr_solve'
                cmd = [
                    mllr, '-meanfn',
                    os.path.join(self.model_dir, 'means'), '-varfn',
                    os.path.join(self.model_dir, 'variances'), '-outmllrfn',
                    os.path.join(self.model_dir, 'mllr_matrix'), '-accumdir',
                    self.working_dir
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode != 0):
                    continue_next = False
                nextcommand = "map"
            if (command == "map"):
                # Update the acoustic model files with MAP
                # In this case, unlike MLLR, we don't create a generic transform, but update each parameter in the model
                # We copy the acoustic model directory and overwrite the new directory with the adapted model files
                if (os.path.isdir(self.adapt_dir)):
                    # Remove the adapt dir
                    shutil.rmtree(self.adapt_dir)
                    response.append("Cleared adapt directory {}".format(
                        self.adapt_dir))
                shutil.copytree(self.model_dir, self.adapt_dir)
                map_adapt = '/usr/lib/sphinxtrain/map_adapt'
                if os.path.isfile('/usr/local/libexec/sphinxtrain/map_adapt'):
                    map_adapt = '/usr/local/libexec/sphinxtrain/map_adapt'
                cmd = [
                    map_adapt, '-moddeffn',
                    os.path.join(self.model_dir,
                                 'mdef.txt'), '-ts2cbfn', '.ptm.', '-meanfn',
                    os.path.join(self.model_dir, 'means'), '-varfn',
                    os.path.join(self.model_dir, 'variances'), '-mixwfn',
                    os.path.join(self.model_dir, 'mixture_weights'), '-tmatfn',
                    os.path.join(self.model_dir, 'transition_matrices'),
                    '-accumdir', self.working_dir, '-mapmeanfn',
                    os.path.join(self.adapt_dir, 'means'), '-mapvarfn',
                    os.path.join(self.adapt_dir, 'variances'), '-mapmixwfn',
                    os.path.join(self.adapt_dir,
                                 'mixture_weights'), '-maptmatfn',
                    os.path.join(self.adapt_dir, 'transition_matrices')
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode != 0):
                    continue_next = False
                nextcommand = "sendump"
            if (command == "sendump"):
                # Recreating the adapted sendump file
                # a sendump file saves space and is supported by pocketsphinx
                mk_s2sendump = '/usr/lib/sphinxtrain/mk_s2sendump'
                if os.path.isfile(
                        '/usr/local/libexec/sphinxtrain/mk_s2sendump'):
                    mk_s2sendump = '/usr/local/libexec/sphinxtrain/mk_s2sendump'
                cmd = [
                    mk_s2sendump, '-pocketsphinx', 'yes', '-moddeffn',
                    os.path.join(self.adapt_dir, 'mdef.txt'), '-mixwfn',
                    os.path.join(self.adapt_dir, 'mixture_weights'),
                    '-sendumpfn',
                    os.path.join(self.adapt_dir, 'sendump')
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode != 0):
                    continue_next = False
                nextcommand = "updateprofile"
            if (command == "updateprofile"):
                # Format the dictionary
                # Remove whitespace at the beginning of each line
                # Remove (#) after first word
                # collapse multiple whitespaces into a single space
                # Remove any whitespaces from the end
                with open(os.path.join(self.adapt_dir, "cmudict.dict"),
                          "r") as in_file:
                    with open(self.formatteddict_path, "w+") as out_file:
                        for line in in_file:
                            # Remove whitespace at beginning and end
                            line = line.strip()
                            # remove the number in parentheses (if there is one)
                            line = re.sub('([^\\(]+)\\(\\d+\\)', '\\1', line)
                            # compress all multiple whitespaces into a single whitespace
                            line = re.sub('\s+', ' ', line)
                            # replace the first whitespace with a tab
                            line = line.replace(' ', '\t', 1)
                            print(line, file=out_file)
                # Use phonetisaurus to prepare an fst model
                cmd = [
                    "phonetisaurus-train", "--lexicon",
                    self.formatteddict_path, "--seq2_del", "--dir_prefix",
                    os.path.join(self.adapt_dir, "train")
                ]
                completedprocess = run_command(cmd)
                response.append(
                    process_completedprocess(completedprocess, output='html'))
                if (completedprocess.returncode == 0):
                    # Now set the values in profile
                    profile.set_profile_var(['pocketsphinx', 'fst_model'],
                                            os.path.join(
                                                self.adapt_dir, "train",
                                                "model.fst"))
                    profile.set_profile_var(['pocketsphinx', 'hmm_dir'],
                                            self.adapt_dir)
                    profile.save_profile()
                    # Also run through the list of words that have been used
                    # that are not the wake word or a command word and make
                    # sure they are all identified so pocketsphinx can match
                    # them and not get confused on word boundaries.
                    # Pull a list of all words spoken from
                    # verified translations
                    query = " ".join([
                        "with recursive split(", " word,", " rest", ") as (",
                        " select", "  '',"
                        "  upper(replace(replace(verified_transcription,'?',''),',','')) || ' '",
                        " from audiolog",
                        " where type in ('active','passive') and reviewed!=''",
                        " union all select",
                        "  substr(rest, 0, instr(rest,' ')),",
                        "  substr(rest,instr(rest,' ')+1)",
                        " from split where rest <> ''"
                        ")",
                        "select word from split where word!='' group by word"
                    ])
                    c.execute(query)
                    words_used = [x[0].upper() for x in c.fetchall()]
                    # Pull the list of words from the local standard phrases
                    keywords = profile.get_profile_var(['keyword'])
                    if (isinstance(keywords, str)):
                        keywords = [keywords]
                    phrases = [keyword.upper() for keyword in keywords]
                    custom_standard_phrases_dir = paths.sub(
                        os.path.join("data", "standard_phrases"))
                    custom_standard_phrases_file = os.path.join(
                        custom_standard_phrases_dir,
                        "{}.txt".format(self.language))
                    if (os.path.isfile(custom_standard_phrases_file)):
                        with open(custom_standard_phrases_file, mode="r") as f:
                            for line in f:
                                phrase = line.strip().upper()
                                if phrase:
                                    phrases.append(phrase)
                    # Get all the phrases that the plugins are looking for
                    ps = pluginstore.PluginStore()
                    ps.detect_plugins("speechhandler")
                    for info in ps.get_plugins_by_category("speechhandler"):
                        try:
                            plugin = info.plugin_class(info,
                                                       profile.get_profile())
                            # get_phrases is vestigial now
                            if (hasattr(plugin, "get_phrases")):
                                for phrase in plugin.get_phrases():
                                    phrases.extend([
                                        word.upper()
                                        for word in phrase.split()
                                    ])
                            # get the phrases from the plugin intents
                            if (hasattr(plugin, "intents")):
                                intents = plugin.intents()
                                for intent in intents:
                                    for template in intents[intent]['locale'][
                                            self.language]['templates']:
                                        phrases.extend([
                                            word.upper()
                                            for word in template.split()
                                        ])
                        except Exception as e:
                            message = "Unknown"
                            if hasattr(e, "message"):
                                message = e.message
                            response.append(
                                "Plugin {} skipped! (Reason: {})".format(
                                    info.name, message))
                            self._logger.warning(
                                "Plugin '{}' skipped! (Reason: {})".format(
                                    info.name, message),
                                exc_info=True)

                    # Get the set of all words in words_used that do not appear
                    # in phrases
                    print("Phrases:")
                    print(phrases)
                    new_phrases = [
                        word for word in words_used if word not in phrases
                    ]
                    response.append("{} new phrases detected".format(
                        len(new_phrases)))
                    description.append("adding {} new phrases".format(
                        len(new_phrases)))
                    if (len(new_phrases) > 0):
                        table = "<table><tr><th>new phrase</th></tr>"
                        # Append the new phrases to the custom
                        # standard_phrases\{language}.txt file
                        if (not os.path.isdir(custom_standard_phrases_dir)):
                            os.makedirs(custom_standard_phrases_dir)
                        with open(custom_standard_phrases_file,
                                  mode="a+") as f:
                            for word in new_phrases:
                                table += "<tr><td>{}</td></tr>".format(word)
                                print(word, file=f)
                        table += "</table>"
                        response.append(table)
                    # Finally, force naomi to regenerate all of the
                    # pocketsphinx vocabularies by deleting all the
                    # vocabularies/{language}/sphinx/{}/revision
                    # files:
                    for revision_file in glob.glob(
                            paths.sub('vocabularies', self.language, 'sphinx',
                                      "*", "revision")):
                        os.remove(revision_file)
                    # Add the description
                    c.execute('''insert into trainings values(?,?,?)''',
                              (datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                               'Adapt Pocketsphinx', " ".join(description)))
                    conn.commit()
                else:
                    continue_next = False
        except Exception as e:
            continue_next = False
            message = "Unknown"
            if hasattr(e, "message"):
                message = e.message
            self._logger.error("Error: {}".format(message), exc_info=True)
            response.append('<span class="failure">{}</span>'.format(message))
        if not continue_next:
            nextcommand = ""
        return response, nextcommand, description
Ejemplo n.º 10
0
    def settings(self):
        language = profile.get(['language'])
        # Get the defaults for settings
        # hmm_dir
        hmm_dir = profile.get(
            ['pocketsphinx', 'hmm_dir']
        )
        if(not hmm_dir):
            # Make a list of possible paths to check
            hmm_dir_paths = [
                os.path.join(
                    os.path.expanduser("~"),
                    "pocketsphinx-python",
                    "pocketsphinx",
                    "model",
                    "en-us",
                    "en-us"
                ),
                os.path.join(
                    os.path.expanduser("~"),
                    "pocketsphinx",
                    "model",
                    "en-us",
                    "en-us"
                ),
                os.path.join(
                    "/",
                    "usr",
                    "share",
                    "pocketsphinx",
                    "model",
                    "en-us",
                    "en-us"
                ),
                os.path.join(
                    "/usr",
                    "local",
                    "share",
                    "pocketsphinx",
                    "model",
                    "hmm",
                    "en_US",
                    "hub4wsj_sc_8k"
                )
            ]
            # see if any of these paths exist
            for path in hmm_dir_paths:
                if os.path.isdir(path):
                    hmm_dir = path
        # fst_model
        fst_model = profile.get_profile_var(["pocketsphinx", "fst_model"])
        if not fst_model:
            # Make a list of possible paths to check
            fst_model_paths = [
                os.path.join(
                    paths.sub(
                        os.path.join(
                            "pocketsphinx",
                            "adapt",
                            "en-US",
                            "train",
                            "model.fst"
                        )
                    )
                ),
                os.path.join(
                    os.path.expanduser("~"),
                    "pocketsphinx-python",
                    "pocketsphinx",
                    "model",
                    "en-us",
                    "train",
                    "model.fst"
                ),
                os.path.join(
                    os.path.expanduser("~"),
                    "cmudict",
                    "train",
                    "model.fst"
                ),
                os.path.join(
                    os.path.expanduser("~"),
                    "CMUDict",
                    "train",
                    "model.fst"
                ),
                os.path.join(
                    os.path.expanduser("~"),
                    "phonetisaurus",
                    "g014b2b.fst"
                )
            ]
            for path in fst_model_paths:
                if os.path.isfile(path):
                    fst_model = path
        # If either the hmm dir or fst model is missing, then
        # download the standard model
        if not(hmm_dir and os.path.isdir(hmm_dir) and fst_model and os.path.isfile(fst_model)):
            # Start by checking to see if we have a copy of the standard
            # model for this user's chosen language and download it if not.
            # Check for the files we need
            language = profile.get_profile_var(['language'])
            base_working_dir = paths.sub("pocketsphinx")
            if not os.path.isdir(base_working_dir):
                os.mkdir(base_working_dir)
            standard_dir = os.path.join(base_working_dir, "standard")
            if not os.path.isdir(standard_dir):
                os.mkdir(standard_dir)
            standard_dir = os.path.join(standard_dir, language)
            if not os.path.isdir(standard_dir):
                os.mkdir(standard_dir)
            hmm_dir = standard_dir
            fst_model = os.path.join(hmm_dir, "train", "model.fst")
            formatteddict_path = os.path.join(
                hmm_dir,
                "cmudict.formatted.dict"
            )
            if(not check_pocketsphinx_model(hmm_dir)):
                # Check and see if we already have a copy of the standard
                # language model
                print("Downloading and installing the {} pocketsphinx language model".format(language))
                cmd = [
                    'git',
                    'clone',
                    '-b',
                    language,
                    'https://github.com/NaomiProject/CMUSphinx_standard_language_models.git',
                    hmm_dir
                ]
                completedprocess = run_command(cmd)
                self._logger.info(process_completedprocess(completedprocess))
            if(not os.path.isfile(formatteddict_path)):
                print("Formatting the g2p dictionary")
                with open(os.path.join(standard_dir, "cmudict.dict"), "r") as in_file:
                    with open(formatteddict_path, "w+") as out_file:
                        for line in in_file:
                            # Remove whitespace at beginning and end
                            line = line.strip()
                            # remove the number in parentheses (if there is one)
                            line = re.sub('([^\\(]+)\\(\\d+\\)', '\\1', line)
                            # compress all multiple whitespaces into a single whitespace
                            line = re.sub('\s+', ' ', line)
                            # replace the first whitespace with a tab
                            line = line.replace(' ', '\t', 1)
                            print(line, file=out_file)
            if(not os.path.isfile(fst_model)):
                # Use phonetisaurus to prepare an fst model
                print("Training an FST model")
                cmd = [
                    "phonetisaurus-train",
                    "--lexicon", formatteddict_path,
                    "--seq2_del",
                    "--dir_prefix", os.path.join(hmm_dir, "train")
                ]
                completedprocess = run_command(cmd)
                self._logger.info(process_completedprocess(completedprocess))

        phonetisaurus_executable = profile.get_profile_var(
            ['pocketsphinx', 'phonetisaurus_executable']
        )
        if(not phonetisaurus_executable):
            if(check_program_exists('phonetisaurus-g2pfst')):
                phonetisaurus_executable = 'phonetisaurus-g2pfst'
            else:
                phonetisaurus_executable = 'phonetisaurus-g2p'
        _ = self.gettext
        return OrderedDict(
            [
                (
                    ('pocketsphinx', 'hmm_dir'), {
                        'title': _('PocketSphinx hmm file'),
                        'description': "".join([
                            _('PocketSphinx hidden markov model directory')
                        ]),
                        'default': hmm_dir
                    }
                ),
                (
                    ('pocketsphinx', 'fst_model'), {
                        'title': _('PocketSphinx FST file'),
                        'description': "".join([
                            _('PocketSphinx finite state transducer file')
                        ]),
                        'default': fst_model
                    }
                ),
                (
                    ('pocketsphinx', 'phonetisaurus_executable'), {
                        'title': _('Phonetisaurus executable'),
                        'description': "".join([
                            _('Phonetisaurus is used to build custom dictionaries')
                        ]),
                        'default': phonetisaurus_executable
                    }
                ),
            ]
        )