Getting started¶
1. Install¶
You need Python 3.8 or newer. Then, in a terminal (Command Prompt or PowerShell on Windows):
This works on Windows, macOS and Linux, and reads WAV, FLAC, OGG and MP3 files.
2. Align one recording¶
balign recording.wav "the words spoken in the recording" --preset=en-us --textgrid=recording.TextGrid
recording.wavis your audio file.- The text in quotes is what is said in it, written out in ordinary spelling.
--preset=en-usis the language: American English here. See step 3 for others.--textgrid=recording.TextGridwrites a TextGrid you can open in Praat, with aphonemestier and awordstier.
BFA also writes recording.vs.json next to the audio, with the same times and more detail.
The first run downloads the models, about 1.3 GB in all, once. After that BFA works offline.
To try it on a recording that comes with BFA, download butterfly.wav and run:
b → 0 ms – 52 ms (confidence: 0.89)
ə → 52 ms – 168 ms (confidence: 0.09)
t → 168 ms – 258 ms (confidence: 0.05)
ɝ → 258 ms – 351 ms (confidence: 0.01)
f → 351 ms – 498 ms (confidence: 0.28)
ɫ → 498 ms – 572 ms (confidence: 0.11)
a → 572 ms – 826 ms (confidence: 0.94)
ɪ → 826 ms – 982 ms (confidence: 0.25)
Phonemes are written in IPA. The confidence says how sure the model is of each phoneme. Compare it between phonemes of one recording, not as an absolute score: a correctly placed phoneme often reads below 0.5 (why).
3. Other languages¶
Change --preset:
en-us English (US) |
en-GB English (UK) |
de German |
fr French |
es Spanish |
pt Portuguese |
it Italian |
nl Dutch |
ru Russian |
uk Ukrainian |
pl Polish |
tr Turkish |
zh Mandarin |
yue Cantonese |
ja Japanese |
ko Korean |
vi Vietnamese |
th Thai |
ar Arabic |
fa Persian |
hi Hindi |
ur Urdu |
el Greek |
sw Swahili |
That is a sample. All 82 languages, and what to know about each (tone, word segmentation), are
on the language support page, or run balign --list-presets.
Japanese, Cantonese and Thai need one extra install step, listed below.
4. A long recording¶
For a recording with many sentences, such as an interview, put the transcript in a JSON file. Give each stretch of speech its start and end time in seconds:
{"segments": [
{"start": 0.0, "end": 4.2, "text": "so where did you grow up"},
{"start": 4.6, "end": 9.8, "text": "in a small town near the coast"}
]}
The times don't need to be exact, but each stretch must contain all of its words (a little silence either side is fine). BFA finds the phonemes inside each stretch. Whisper and most speech-to-text tools write this format (with extra fields, which BFA ignores).
5. From Python¶
from bournemouth_aligner import BFA
aligner = BFA(preset="en-us")
audio = aligner.load_audio("butterfly.wav")
result = aligner.process_sentence("butterfly", audio)
for ph in result["segments"][0]["phoneme_ts"]:
print(ph["ipa_label"], round(ph["start_ms"]), round(ph["end_ms"]), round(ph["confidence"], 2))
aligner.convert_to_textgrid(result, output_file="butterfly.TextGrid")
For a transcript file: aligner.process_srt_file("interview.json", "interview.wav", ts_out_path="interview.vs.json").
More in examples/ and the developer guide, which also explains
every field of the JSON output.
Many files at once¶
Aligning all your files in one call is much faster than one call per file. With a GPU, also set
n_jobs=4: the alignment search then runs in 4 worker processes while the GPU works on the next
batch. The output is the same.
import json
from bournemouth_aligner import BFA
if __name__ == "__main__": # needed when n_jobs > 1
aligner = BFA(preset="en-us", n_jobs=4)
names = ["interview1", "interview2"]
transcripts = [json.load(open(n + ".json")) for n in names]
audio = [aligner.load_audio(n + ".wav") for n in names]
results = aligner.process_segments(transcripts, audio) # one call for every file
for name, result in zip(names, results):
aligner.convert_to_textgrid(result, output_file=name + ".TextGrid")
Tips for good results¶
- The text must match the audio. Every phoneme of the text is placed somewhere, so a word that
isn't spoken gets squeezed into its neighbours. Segments that went badly are flagged in the
JSON as
coverage_analysis["bad_alignment"]. - Write numbers as words. "twenty five", not "25". The same goes for abbreviations and symbols.
- Shorter stretches align better. A stretch of up to 2 minutes is aligned in one pass, and longer ones are handled automatically, but a transcript split into sentences works best.
- The phonemes come from a dictionary-style pronunciation of the text, not from what the speaker actually said. Reduced or dialectal pronunciations are aligned against the standard form.
- Conversational vs read speech. The default setting is tuned for conversational speech. For
each phoneme's own onset, rather than the boundaries as a whole, try
--boundary-bonus-weight=0.
Optional extras¶
pip install "bournemouth-forced-aligner[ja]" # Japanese word segmentation (fugashi + unidic-lite)
pip install "bournemouth-forced-aligner[yue]" # Cantonese word segmentation (pycantonese)
pip install "bournemouth-forced-aligner[th]" # Thai word segmentation (pythainlp)
pip install "bournemouth-forced-aligner[ph66]" # the espeak-ng phoneme set; also needs the espeak-ng program
pip install "bournemouth-forced-aligner[audio]" # other audio formats (m4a, aac, mp4); also needs FFmpeg
The latest code from GitHub: pip install git+https://github.com/speech-industry/bournemouth-forced-aligner.git.
Phoneme sets¶
BFA has two phoneme sets, each with its own models:
phoneme_set |
Pronunciations from | Models | Languages |
|---|---|---|---|
std (default) |
standard_g2p, a neural G2P model | one per writing system: mbfa-latin, mbfa-cyrillic, mbfa-cjk, mbfa-japanese, mbfa-vietnamese, mbfa-thai, mbfa-abjad, mbfa-brahmic, mbfa-other-alphabetic |
82, with tone, stress and length labels |
ph66 |
espeak-ng, reduced to 66 phonemes | CUPE-3-en (English), CUPE-3-mls7 (everything else) |
80+, trained on English and 7 European languages |
balign recording.wav "hello world" --preset=en-us --phoneme-set=ph66 # the most accurate English model
balign recording.wav "<Bengali text>" --preset=bn --phoneme-set=ph66 # Bengali: only ph66 has it so far
Settings worth knowing¶
| Python / command line | |
|---|---|
boundary_bonus_weight / --boundary-bonus-weight |
How strongly boundaries are pulled towards sudden changes in the sound. The default, 15, is set for conversational speech. 0 gives better onsets for individual phonemes. |
duration_max=10 |
Up to 2.6× faster on short clips, same result. Long segments are never cut. |
n_jobs=4 |
Run the alignment search in 4 worker processes when one call aligns many segments (process_segments, process_srt_file). With a GPU: 2.4× faster on long recordings. Little effect on a CPU. Same output; your script needs if __name__ == "__main__":. |
low_confidence_level |
The confidence below which a phoneme counts as doubtful when flagging bad segments. |
punctuation_sil=False |
Stop writing a pause at commas and full stops (on by default; default phoneme set only). Without it, a pause is swallowed by the phone next to it. |
warn_level=0 |
Print nothing, downloads included. |
All options: balign --help, and the developer guide.
Upgrading from 1.x¶
BFA 2.0 is not compatible with 1.x. The acoustic models are new: no CTC, no blank class, 5 ms
frames, and a multilingual model family that is now the default. The alignment method has
changed too: a segmental Viterbi, the same decoder the models were trained with, places each
boundary where two phoneme posteriors cross, instead of snapping CTC spikes onto onsets. v1
models do not load, and the output and constructor arguments have changed: see the
CHANGELOG. To keep using 1.x, pin it: pip install "bournemouth-forced-aligner<2".