Debugging checklist
Describe the issue
A clear and concise description of what the bug is.
I was trying to align wavs using python interfaces without CLI, since I need an end-to-end application. Thanks to this issue#885 in MFA, I have written a small script to test it like the following (Adapted from mfa_align_one).
"""Command line functions for aligning single files"""
from __future__ import annotations
from pathlib import Path
import pywrapfst
import rich_click as click
from kalpy.aligner import KalpyAligner
from kalpy.feat.cmvn import CmvnComputer
from kalpy.fstext.lexicon import HierarchicalCtm, LexiconCompiler
from kalpy.utterance import Segment
from kalpy.utterance import Utterance as KalpyUtterance
from montreal_forced_aligner import config
from montreal_forced_aligner.alignment import PretrainedAligner
from montreal_forced_aligner.corpus.classes import FileData
from montreal_forced_aligner.data import (
BRACKETED_WORD,
CUTOFF_WORD,
LAUGHTER_WORD,
OOV_WORD,
Language,
)
from montreal_forced_aligner.dictionary.mixins import (
DEFAULT_BRACKETS,
DEFAULT_CLITIC_MARKERS,
DEFAULT_COMPOUND_MARKERS,
DEFAULT_PUNCTUATION,
DEFAULT_WORD_BREAK_MARKERS,
)
from montreal_forced_aligner.models import AcousticModel, G2PModel
from montreal_forced_aligner.online.alignment import tokenize_utterance_text
from montreal_forced_aligner.tokenization.simple import SimpleTokenizer
from montreal_forced_aligner.tokenization.spacy import generate_language_tokenizer
import librosa
def test_align_one(
**kwargs,
):
"""
Align a single file with a pronunciation dictionary and a pretrained acoustic model.
"""
config_path = kwargs.get("config_path", None)
sound_file_path: Path = kwargs["sound_file_path"]
text_file_path: Path = kwargs["text_file_path"]
dictionary_path: Path = kwargs["dictionary_path"]
acoustic_model_path = kwargs["acoustic_model_path"]
output_path: Path = kwargs["output_path"]
if output_path.is_dir():
output_path = output_path.joinpath(sound_file_path.stem + ".TextGrid")
output_format = kwargs.get("output_format", "long_textgrid")
no_tokenization = kwargs.get("no_tokenization", False)
g2p_model_path = kwargs.get("g2p_model_path", None)
acoustic_model = AcousticModel(acoustic_model_path)
g2p_model = None
if g2p_model_path:
g2p_model = G2PModel(g2p_model_path)
args = {"clean": True}
c = PretrainedAligner.parse_parameters(config_path, args=args)
extracted_models_dir = config.TEMPORARY_DIRECTORY.joinpath("extracted_models", "dictionary")
dictionary_directory = extracted_models_dir.joinpath(dictionary_path.stem)
dictionary_directory.mkdir(parents=True, exist_ok=True)
lexicon_compiler = LexiconCompiler(
disambiguation=False,
silence_probability=acoustic_model.parameters["silence_probability"],
initial_silence_probability=acoustic_model.parameters["initial_silence_probability"],
final_silence_correction=acoustic_model.parameters["final_silence_correction"],
final_non_silence_correction=acoustic_model.parameters["final_non_silence_correction"],
silence_phone=acoustic_model.parameters["optional_silence_phone"],
oov_phone=acoustic_model.parameters["oov_phone"],
position_dependent_phones=acoustic_model.parameters["position_dependent_phones"],
phones=acoustic_model.parameters["non_silence_phones"],
ignore_case=c.get("ignore_case", True),
)
l_fst_path = dictionary_directory.joinpath("L.fst")
l_align_fst_path = dictionary_directory.joinpath("L_align.fst")
words_path = dictionary_directory.joinpath("words.txt")
phones_path = dictionary_directory.joinpath("phones.txt")
if l_fst_path.exists() and not config.CLEAN:
lexicon_compiler.load_l_from_file(l_fst_path)
lexicon_compiler.load_l_align_from_file(l_align_fst_path)
lexicon_compiler.word_table = pywrapfst.SymbolTable.read_text(words_path)
lexicon_compiler.phone_table = pywrapfst.SymbolTable.read_text(phones_path)
else:
lexicon_compiler.load_pronunciations(dictionary_path)
lexicon_compiler.create_fsts()
lexicon_compiler.clear()
if no_tokenization or acoustic_model.language is Language.unknown:
tokenizer = SimpleTokenizer(
word_table=lexicon_compiler.word_table,
word_break_markers=c.get("word_break_markers", DEFAULT_WORD_BREAK_MARKERS),
punctuation=c.get("punctuation", DEFAULT_PUNCTUATION),
clitic_markers=c.get("clitic_markers", DEFAULT_CLITIC_MARKERS),
compound_markers=c.get("compound_markers", DEFAULT_COMPOUND_MARKERS),
brackets=c.get("brackets", DEFAULT_BRACKETS),
laughter_word=c.get("laughter_word", LAUGHTER_WORD),
oov_word=c.get("oov_word", OOV_WORD),
bracketed_word=c.get("bracketed_word", BRACKETED_WORD),
cutoff_word=c.get("cutoff_word", CUTOFF_WORD),
ignore_case=c.get("ignore_case", True),
)
else:
tokenizer = generate_language_tokenizer(acoustic_model.language)
file_name = sound_file_path.stem
file = FileData.parse_file(file_name, sound_file_path, text_file_path, "", 0)
file_ctm = HierarchicalCtm([])
utterances = []
cmvn_computer = CmvnComputer()
for utterance in file.utterances:
seg = Segment(None, utterance.begin, utterance.end, utterance.channel)
print(f"{utterance}")
audio = librosa.load(sound_file_path, sr=16000,)[0]
if audio.ndim > 1:
audio = audio.mean(axis=0)
seg._wave = audio
normalized_text = tokenize_utterance_text(
utterance.text,
lexicon_compiler,
tokenizer,
g2p_model,
language=acoustic_model.language,
)
utt = KalpyUtterance(seg, normalized_text)
utt.generate_mfccs(acoustic_model.mfcc_computer)
utterances.append(utt)
cmvn = cmvn_computer.compute_cmvn_from_features([utt.mfccs for utt in utterances])
align_options = {
k: v
for k, v in c.items()
if k
in [
"beam",
"retry_beam",
"acoustic_scale",
"transition_scale",
"self_loop_scale",
"boost_silence",
]
}
if g2p_model is not None or not (l_fst_path.exists() and not config.CLEAN):
lexicon_compiler.fst.write(str(l_fst_path))
lexicon_compiler.align_fst.write(str(l_align_fst_path))
lexicon_compiler.word_table.write_text(words_path)
lexicon_compiler.phone_table.write_text(phones_path)
kalpy_aligner = KalpyAligner(acoustic_model, lexicon_compiler, **align_options)
for utt in utterances:
utt.apply_cmvn(cmvn)
ctm = kalpy_aligner.align_utterance(utt)
file_ctm.word_intervals.extend(ctm.word_intervals)
if str(output_path) != "-":
output_path.parent.mkdir(parents=True, exist_ok=True)
file_ctm.export_textgrid(
output_path, file_duration=file.wav_info.duration, output_format=output_format
)
if __name__ == "__main__":
from montreal_forced_aligner.models import DictionaryModel, AcousticModel
dict_path = DictionaryModel.get_pretrained_path("english_us_arpa")
acoustic_path = AcousticModel.get_pretrained_path("english_us_arpa")
test_align_one(
dictionary_path=dict_path,
acoustic_model_path=acoustic_path,
sound_file_path=Path("test2/wavs/test_short1_1.wav"),
text_file_path=Path("test2/wavs/test_short1_1.txt"),
output_path=Path("test2/wavs/test_short1_1.TextGrid"),
)
This works fine and can return some results. However, if I import torch before this program or specifically import it before from kalpy.aligner import KalpyAligner like this
import torch
from kalpy.aligner import KalpyAligner
the program would raise an error
File "/home/user/Montreal-Forced-Aligner/test2/test_align.py", line 9, in <module>
from kalpy.aligner import KalpyAligner
File "/data2/user/Miniconda3/envs/dub3/lib/python3.11/site-packages/kalpy/aligner.py", line 6, in <module>
from _kalpy.gmm import gmm_interpolate_boundary_fast
ImportError: /data2/user/Miniconda3/envs/dub3/lib/python3.11/site-packages/../.././libcusolver.so.11: undefined symbol: cublasSetEnvironmentMode, version libcublas.so.12
Although temporary workarounds such as downgrading CUDA or changing the import order (late importing) can resolve the issue, these solutions conflict with my project requirements. I would prefer a stable solution that allows both torch and MFA/Kalpy to coexist within the same Python process.
PS: simply installing cpu version can solve this but I would like to keep this issue as a report.
conda install -c conda-forge kaldi=*=*cpu*
For Reproducing your issue
Please fill out the following:
- Corpus structure
- What language is the corpus in?
English.
- How many files/speakers?
1 file with only 1 speaker.
- Are you using lab files or TextGrid files for input?
Yes.
- Dictionary
- Are you using a dictionary from MFA? If so, which one?
english_us_arpa
- If it's a custom dictionary, what is the phoneset?
- Acoustic model
- If you're using an acoustic model, is it one download through MFA? If so, which one?
english_us_arpa
- If it's a model you've trained, what data was it trained on?
Log file
No logs are created since it is a python program.
Desktop (please complete the following information):
- OS: [e.g. Windows, OSX, Linux]
- Version [e.g. MacOSX 10.15, Ubuntu 20.04, Windows 10, etc]
- Any other details about the setup (Cloud, Docker, etc)
Ubuntu 24.04 x86_64. Pytorch version 2.8.0 with CUDA 12.6. Run directly through python **.py.
Installing MFA through
conda install -c conda-forge montreal-forced-aligner
Additional context
Add any other context about the problem here.
Debugging checklist
--cleanflag?Describe the issue
A clear and concise description of what the bug is.
I was trying to align wavs using python interfaces without CLI, since I need an end-to-end application. Thanks to this issue#885 in MFA, I have written a small script to test it like the following (Adapted from mfa_align_one).
This works fine and can return some results. However, if I import
torchbefore this program or specifically import it beforefrom kalpy.aligner import KalpyAlignerlike thisthe program would raise an error
Although temporary workarounds such as downgrading CUDA or changing the import order (late importing) can resolve the issue, these solutions conflict with my project requirements. I would prefer a stable solution that allows both torch and MFA/Kalpy to coexist within the same Python process.
PS: simply installing cpu version can solve this but I would like to keep this issue as a report.
For Reproducing your issue
Please fill out the following:
English.
1 file with only 1 speaker.
Yes.
english_us_arpaenglish_us_arpaLog file
No logs are created since it is a python program.
Desktop (please complete the following information):
Ubuntu 24.04 x86_64. Pytorch version
2.8.0with CUDA12.6. Run directly throughpython **.py.Installing MFA through
Additional context
Add any other context about the problem here.