ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
142 lines
5.0 KiB
Python
142 lines
5.0 KiB
Python
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||
#
|
||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
# You may obtain a copy of the License at
|
||
#
|
||
# http://www.apache.org/licenses/LICENSE-2.0
|
||
#
|
||
# Unless required by applicable law or agreed to in writing, software
|
||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
|
||
import itertools
|
||
import os
|
||
from argparse import ArgumentParser
|
||
from typing import Dict
|
||
|
||
from syllabify import syllabify
|
||
|
||
|
||
"""
|
||
Usage:
|
||
cd NeMo/scripts && python dataset_processing/g2p/convert_cmu_arpabet_to_ipa.py
|
||
"""
|
||
|
||
|
||
def parse_args():
|
||
parser = ArgumentParser("ARPABET to IPA conversion sctipt")
|
||
parser.add_argument(
|
||
'--cmu_arpabet',
|
||
help="Path to CMU ARPABET dictionary file",
|
||
type=str,
|
||
default="tts_dataset_files/cmudict-0.7b_nv22.10",
|
||
)
|
||
parser.add_argument("--ipa_out", help="Path to save IPA version of the dictionary", type=str, required=True)
|
||
parser.add_argument(
|
||
"--mapping",
|
||
help="ARPABET to IPA phoneme mapping file",
|
||
type=str,
|
||
default="tts_dataset_files/cmudict-arpabet_to_ipa_nv22.10.tsv",
|
||
)
|
||
return parser.parse_args()
|
||
|
||
|
||
def convert_arp_to_ipa(arp_to_ipa_dict: Dict[str, str], arp_input: str, remove_space: bool = False) -> str:
|
||
"""
|
||
Converts ARPABET phoneme to IPA based on arp_to_ipa_dict mapping
|
||
|
||
Args:
|
||
arp_to_ipa_dict: ARPABET to IPA phonemes mapping
|
||
arp_input: ARPABET input
|
||
remove_space: set to TRUE to remove spaces between IPA phonemes
|
||
|
||
Returns:
|
||
input word in IPA form
|
||
"""
|
||
|
||
primary_stress = "ˈ"
|
||
secondary_stress = "ˌ"
|
||
stress_dict = {"0": "", "1": primary_stress, "2": secondary_stress}
|
||
|
||
word_ipa = ""
|
||
phonemes = arp_input.split()
|
||
|
||
# split ARPABET phoneme input into syllables,
|
||
# e.g. syllabify(["HH", "AH0", "L", "OW1"]) -> [(['HH'], ['AH0'], []), (['L'], ['OW1'], [])]
|
||
syllables = syllabify(phonemes)
|
||
|
||
for syl_idx, syll in enumerate(syllables):
|
||
syll_stress = ""
|
||
syll_ipa = ""
|
||
|
||
# syll is a tuple of lists of phonemes, here we flatten it and get rid of empty entries,
|
||
# e.g. (['HH'], ['AH0'], []) -> ['HH', 'AH0']
|
||
syll = [x for x in itertools.chain.from_iterable(syll)]
|
||
for phon_idx, phon in enumerate(syll):
|
||
if phon[-1].isdigit():
|
||
syll_stress = phon[-1]
|
||
if syll_stress not in stress_dict:
|
||
raise ValueError(f"{syll_stress} unknown")
|
||
syll_stress = stress_dict[syll_stress]
|
||
|
||
# some phonemes are followed by a digit that represents stress, e.g., `AH0`
|
||
if phon not in arp_to_ipa_dict and phon[-1].isdigit():
|
||
phon = phon[:-1]
|
||
|
||
if phon not in arp_to_ipa_dict:
|
||
raise ValueError(f"|{phon}| phoneme not found in |{arp_input}|")
|
||
else:
|
||
ipa_phone = arp_to_ipa_dict[phon]
|
||
syll_ipa += ipa_phone + " "
|
||
|
||
word_ipa += " " + syll_stress + syll_ipa.strip()
|
||
|
||
word_ipa = word_ipa.strip()
|
||
if remove_space:
|
||
word_ipa = word_ipa.replace(" ", "")
|
||
return word_ipa
|
||
|
||
|
||
def _get_arpabet_to_ipa_mapping(arp_ipa_map_file: str) -> Dict[str, str]:
|
||
"""
|
||
arp_ipa_map_file: Arpabet to IPA phonemes mapping
|
||
"""
|
||
arp_to_ipa = {}
|
||
with open(arp_ipa_map_file, "r", encoding="utf-8") as f:
|
||
for line in f:
|
||
arp, ipa = line.strip().split("\t")
|
||
arp_to_ipa[arp] = ipa
|
||
return arp_to_ipa
|
||
|
||
|
||
def convert_cmu_arpabet_to_ipa(arp_ipa_map_file: str, arp_dict_file: str, output_ipa_file: str):
|
||
"""
|
||
Converts CMU ARPABET-based dictionary to IPA.
|
||
|
||
Args:
|
||
arp_ipa_map_file: ARPABET to IPA phoneme mapping file
|
||
arp_dict_file: path to ARPABET version of CMU dictionary
|
||
output_ipa_file: path to output IPA version of CMU dictionary
|
||
"""
|
||
arp_to_ipa_dict = _get_arpabet_to_ipa_mapping(arp_ipa_map_file)
|
||
with open(arp_dict_file, "r", encoding="utf-8") as f_arp, open(output_ipa_file, "w", encoding="utf-8") as f_ipa:
|
||
for line in f_arp:
|
||
if line.startswith(";;;"):
|
||
f_ipa.write(line)
|
||
else:
|
||
# First, split the line at " #" if there are comments in the dictionary file following the mapping entries.
|
||
# Next, split at default " " separator.
|
||
graphemes, phonemes = line.split(" #")[0].strip().split(" ")
|
||
ipa_form = convert_arp_to_ipa(arp_to_ipa_dict, phonemes, remove_space=True)
|
||
f_ipa.write(f"{graphemes} {ipa_form}\n")
|
||
|
||
print(f"IPA version of {os.path.abspath(arp_dict_file)} saved in {os.path.abspath(output_ipa_file)}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
args = parse_args()
|
||
convert_cmu_arpabet_to_ipa(args.mapping, args.cmu_arpabet, args.ipa_out)
|