fixed book markers

This commit is contained in:
2026-02-26 12:09:43 -07:00
parent 6cefc3c862
commit 44bc757f3f
4 changed files with 156 additions and 506 deletions

View File

@ -4,12 +4,17 @@ audiobook_nem.py
Generate the Book of the Nem audiobook — one unique voice per book/section. Generate the Book of the Nem audiobook — one unique voice per book/section.
Usage: Usage:
python audiobook_nem.py python create_audiobook_nem.py # all enabled books
python create_audiobook_nem.py --list # list available book labels
python create_audiobook_nem.py Introduction
python create_audiobook_nem.py "Book of Hagoth"
python create_audiobook_nem.py Introduction "Book of Hagoth"
To skip a section, comment out its entry in BOOKS below. To permanently skip a section, comment out its entry in BOOKS below.
Output .wav files are written to OUTPUT_DIR (created automatically). Output .wav files are written to OUTPUT_DIR (created automatically).
""" """
import argparse
import re import re
import time import time
import numpy as np import numpy as np
@ -41,30 +46,30 @@ LANG_CODE = "a" # 'a' = American English
# am_santa American male [downloaded] (not used) # am_santa American male [downloaded] (not used)
# ── Book definitions ─────────────────────────────────────────────────────────── # ── Book definitions ───────────────────────────────────────────────────────────
# Format: (label, start_marker, voice, output_wav) # Format: (label, (start_line1, start_line2), voice, output_wav)
# start_marker exact text of the FIRST line of the section header in the source # start_line1 exact text of the FIRST line of the section header
# (leading/trailing whitespace is ignored when matching) # start_line2 prefix of the SECOND line (used together for unambiguous matching)
# voice Kokoro voice name # voice Kokoro voice name
# output_wav filename saved inside OUTPUT_DIR # output_wav filename saved inside OUTPUT_DIR
# #
# Comment out any line to skip that section entirely. # Comment out any line to skip that section entirely.
BOOKS = [ BOOKS = [
# label start_marker voice output_wav # label (start_line1, start_line2) voice output_wav
("Introduction", "Introduction", "af_heart", "00_introduction.wav"), ("Introduction", ("Introduction", "The Book of the Nem"), "af_heart", "00_introduction.wav"),
("Book of Hagoth", "THE BOOK OF HAGOTH", "am_fenrir", "01_hagoth.wav"), ("Book of Hagoth", ("THE BOOK OF HAGOTH", "THE SON OF HAGMENI,"), "am_fenrir", "01_hagoth.wav"),
# ("Shi-Tugo I", "THE FIRST BOOK OF SHI-TUGO", "am_eric", "02_shi_tugo_1.wav"), ("Shi-Tugo I", ("THE FIRST BOOK OF SHI-TUGO", "FORMER WARRIOR, AMMONITE"), "am_eric", "02_shi_tugo_1.wav"),
# ("Sanempet", "THE BOOK OF SANEMPET", "am_liam", "03_sanempet.wav"), ("Sanempet", ("THE BOOK OF SANEMPET", "THE SON OF HAGMENI,"), "am_liam", "03_sanempet.wav"),
# ("Oug", "THE BOOK OF OUG", "am_michael", "04_oug.wav"), ("Oug", ("THE BOOK OF OUG", "THE SON OF SANEMPET"), "am_michael", "04_oug.wav"),
# ("Temple Writings of Oug", "THE BOOK OF", "am_michael", "05_temple_writings_oug.wav"), ("Temple Writings of Oug", ("THE BOOK OF", "THE TEMPLE WRITINGS"), "am_michael", "05_temple_writings_oug.wav"),
# ("Sacred Temple Writings", "THE SACRED", "am_michael", "06_sacred_temple_writings.wav"), ("Sacred Temple Writings", ("THE SACRED", "TEMPLE WRITINGS"), "am_michael", "06_sacred_temple_writings.wav"),
# ("Samuel the Lamanite I", "THE FIRST BOOK", "am_echo", "07_samuel_lamanite_1.wav"), ("Samuel the Lamanite I", ("THE FIRST BOOK", "OF SAMUEL THE LAMANITE"), "am_echo", "07_samuel_lamanite_1.wav"),
# ("Samuel the Lamanite II", "THE SECOND BOOK", "am_echo", "08_samuel_lamanite_2.wav"), ("Samuel the Lamanite II", ("THE SECOND BOOK", "OF SAMUEL THE LAMANITE"), "am_echo", "08_samuel_lamanite_2.wav"),
# ("Manti", "THE BOOK OF MANTI", "am_onyx", "09_manti.wav"), ("Manti", ("THE BOOK OF MANTI", "THE SON OF OUG"), "am_onyx", "09_manti.wav"),
# ("Pa Nat I", "THE FIRST BOOK OF PA NAT", "af_nicole", "10_pa_nat_1.wav"), ("Pa Nat I", ("THE FIRST BOOK OF PA NAT", "THE DAUGHTER OF SHIMLEI"), "af_nicole", "10_pa_nat_1.wav"),
# ("Moroni I", "THE FIRST BOOK OF MORONI", "am_adam", "11_moroni_1.wav"), ("Moroni I", ("THE FIRST BOOK OF MORONI", "THE SON OF MORMON,"), "am_adam", "11_moroni_1.wav"),
# ("Moroni II", "THE SECOND BOOK OF MORONI", "am_adam", "12_moroni_2.wav"), ("Moroni II", ("THE SECOND BOOK OF MORONI", "THE SON OF MORMON,"), "am_adam", "12_moroni_2.wav"),
# ("Moroni III", "THE THIRD BOOK OF MORONI", "am_adam", "13_moroni_3.wav"), ("Moroni III", ("THE THIRD BOOK OF MORONI", "THE SON OF MORMON,"), "am_adam", "13_moroni_3.wav"),
# ("Shioni", "THE BOOK OF SHIONI", "am_puck", "14_shioni.wav"), ("Shioni", ("THE BOOK OF SHIONI", "THE SON OF MORONI"), "am_puck", "14_shioni.wav"),
] ]
# ── Helpers ──────────────────────────────────────────────────────────────────── # ── Helpers ────────────────────────────────────────────────────────────────────
@ -72,23 +77,24 @@ BOOKS = [
def load_and_split(source: Path, books: list) -> dict[str, str]: def load_and_split(source: Path, books: list) -> dict[str, str]:
""" """
Read the source file and split it into sections keyed by label. Read the source file and split it into sections keyed by label.
Each section starts at its start_marker line and ends just before the Each section starts at its (start_line1, start_line2) marker pair and
next section's start_marker. ends just before the next section's marker.
""" """
raw_lines = source.read_text(encoding="utf-8").splitlines() raw_lines = source.read_text(encoding="utf-8").splitlines()
# Build a mapping: marker_text → index in BOOKS # Build a mapping: (label, line1, line2) for each book
markers = [(label, marker.strip()) for label, marker, _, _ in books] markers = [(label, m[0].strip(), m[1].strip()) for label, m, _, _ in books]
# Find the line index of each marker's first occurrence # Find the line index of each marker's first occurrence (two-line match)
marker_positions: list[tuple[int, int]] = [] # (line_idx, books_idx) marker_positions: list[tuple[int, int]] = [] # (line_idx, books_idx)
for book_idx, (label, marker) in enumerate(markers): for book_idx, (label, m1, m2) in enumerate(markers):
for line_idx, line in enumerate(raw_lines): for line_idx, line in enumerate(raw_lines[:-1]):
if line.strip() == marker: if (line.strip() == m1 and
raw_lines[line_idx + 1].strip().startswith(m2)):
marker_positions.append((line_idx, book_idx)) marker_positions.append((line_idx, book_idx))
break break
else: else:
print(f" ⚠ Marker not found for '{label}': '{marker}' — skipping") print(f" ⚠ Marker not found for '{label}': '{m1}' / '{m2}' — skipping")
marker_positions.sort(key=lambda x: x[0]) marker_positions.sort(key=lambda x: x[0])
@ -154,6 +160,38 @@ def generate_audio(pipeline: KPipeline, text: str, voice: str,
# ── Main ─────────────────────────────────────────────────────────────────────── # ── Main ───────────────────────────────────────────────────────────────────────
def main() -> None: def main() -> None:
# ── CLI ────────────────────────────────────────────────────────────
parser = argparse.ArgumentParser(description="Generate Nem audiobook sections.")
parser.add_argument(
"books", nargs="*",
help="Labels of sections to generate (default: all enabled books). "
"Use --list to see available labels."
)
parser.add_argument(
"--list", action="store_true",
help="Print all enabled book labels and exit."
)
args = parser.parse_args()
enabled_labels = [label for label, _, _, _ in BOOKS]
if args.list:
print("Enabled books:")
for label in enabled_labels:
print(f" {label}")
return
# Filter to requested subset, preserving BOOKS order
if args.books:
unknown = [b for b in args.books if b not in enabled_labels]
if unknown:
print(f"Unknown book label(s): {', '.join(unknown)}")
print(f"Run with --list to see available labels.")
return
run_books = [b for b in BOOKS if b[0] in args.books]
else:
run_books = list(BOOKS)
device = "cuda" if torch.cuda.is_available() else "cpu" device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Device: {device}") print(f"Device: {device}")
if device == "cuda": if device == "cuda":
@ -164,8 +202,10 @@ def main() -> None:
print(f"\nSource: '{SOURCE_FILE}'" print(f"\nSource: '{SOURCE_FILE}'"
+ (" ✓ (TTS fixed)" if SOURCE_FILE == _FIXED_FILE else + (" ✓ (TTS fixed)" if SOURCE_FILE == _FIXED_FILE else
" ⚠ (original — run 'Apply Fixes to Text' in the GUI to use phonetic fixes)")) " ⚠ (original — run 'Apply Fixes to Text' in the GUI to use phonetic fixes)"))
# Always split using ALL books for correct section boundaries,
# but only generate for run_books.
sections = load_and_split(SOURCE_FILE, BOOKS) sections = load_and_split(SOURCE_FILE, BOOKS)
print(f" Found {len(sections)} sections.\n") print(f" Found {len(sections)} sections ({len(run_books)} selected).\n")
print("Initialising Kokoro pipeline …") print("Initialising Kokoro pipeline …")
pipeline = KPipeline(lang_code=LANG_CODE) pipeline = KPipeline(lang_code=LANG_CODE)
@ -173,14 +213,26 @@ def main() -> None:
# Pre-compute char counts for all sections so we can estimate ETAs # Pre-compute char counts for all sections so we can estimate ETAs
section_chars: dict[str, int] = { section_chars: dict[str, int] = {
label: len(clean_text(sections[label])) label: len(clean_text(sections[label]))
for label, _, _, _ in BOOKS for label, _, _, _ in run_books
if label in sections if label in sections
} }
# Print char count summary before starting
print(f"\n{'' * 52}")
print(f" {'Section':<30} {'Chars':>8}")
print(f"{'' * 52}")
for label, _, _, wav_name in run_books:
if label in section_chars:
print(f" {label:<30} {section_chars[label]:>8,}")
print(f"{'' * 52}")
total_chars = sum(section_chars.values())
print(f" {'TOTAL':<30} {total_chars:>8,}")
print()
chars_per_sec: float | None = None # derived from the first book that finishes chars_per_sec: float | None = None # derived from the first book that finishes
timing_rows: list[tuple[str, int, float]] = [] # (label, chars, elapsed) timing_rows: list[tuple[str, int, float]] = [] # (label, chars, elapsed)
for label, marker, voice, wav_name in BOOKS: for label, _marker, voice, wav_name in run_books:
if label not in sections: if label not in sections:
continue continue

View File

@ -84,8 +84,11 @@ MAUVE = "#cba6f7"
def play_async(path: Path) -> None: def play_async(path: Path) -> None:
sd.stop() sd.stop()
def _play(): def _play():
data, sr = sf.read(str(path), dtype="float32") try:
sd.play(data, sr) data, sr = sf.read(str(path), dtype="float32")
sd.play(data, sr)
except Exception as exc:
print(f"[audio] playback error: {exc}")
threading.Thread(target=_play, daemon=True).start() threading.Thread(target=_play, daemon=True).start()
@ -119,11 +122,14 @@ def synth_and_play(text: str, on_ready=None) -> None:
*on_ready(path)* is called on the same thread once the file is written. *on_ready(path)* is called on the same thread once the file is written.
""" """
def _run(): def _run():
path = _synth_to_cache(text) try:
if path: path = _synth_to_cache(text)
if on_ready: if path:
on_ready(path) if on_ready:
play_async(path) on_ready(path)
play_async(path)
except Exception as exc:
print(f"[synth] error synthesising '{text}': {exc}")
threading.Thread(target=_run, daemon=True).start() threading.Thread(target=_run, daemon=True).start()
@ -216,6 +222,8 @@ class ProperNounAuditor(tk.Tk):
self._build_ui() self._build_ui()
self._refresh_all() self._refresh_all()
self._alive = True
self.protocol("WM_DELETE_WINDOW", self._on_close)
# Window-level hotkeys (work even when a listbox has keyboard focus) # Window-level hotkeys (work even when a listbox has keyboard focus)
self.bind("<space>", lambda e: self._replay()) self.bind("<space>", lambda e: self._replay())
@ -224,6 +232,19 @@ class ProperNounAuditor(tk.Tk):
if self.focus_get() is not self._fix_entry else None) if self.focus_get() is not self._fix_entry else None)
self.bind("<Escape>", lambda e: self._reset_fix_entry()) self.bind("<Escape>", lambda e: self._reset_fix_entry())
def _on_close(self) -> None:
self._alive = False
sd.stop()
self.destroy()
def _safe_after(self, ms: int, func) -> None:
"""Schedule func on the Tk thread; silently no-ops if window is gone."""
if self._alive:
try:
self.after(ms, func)
except RuntimeError:
pass
# ── UI construction ──────────────────────────────────────────────────────── # ── UI construction ────────────────────────────────────────────────────────
def _build_ui(self) -> None: def _build_ui(self) -> None:
@ -457,7 +478,7 @@ class ProperNounAuditor(tk.Tk):
self.fix_var.set(replacement) self.fix_var.set(replacement)
self.now_playing_var.set(f"{replacement}") self.now_playing_var.set(f"{replacement}")
def _on_ready(_path): def _on_ready(_path):
self.after(0, lambda: self.now_playing_var.set(replacement)) self._safe_after(0, lambda: self.now_playing_var.set(replacement))
synth_and_play(replacement, on_ready=_on_ready) synth_and_play(replacement, on_ready=_on_ready)
else: else:
# Correct list — show word in fix entry, play it # Correct list — show word in fix entry, play it
@ -515,7 +536,7 @@ class ProperNounAuditor(tk.Tk):
target.unlink() target.unlink()
self.now_playing_var.set(f"… regen {fix_text}") self.now_playing_var.set(f"… regen {fix_text}")
def _on_ready(_p): def _on_ready(_p):
self.after(0, lambda: self.now_playing_var.set(fix_text)) self._safe_after(0, lambda: self.now_playing_var.set(fix_text))
synth_and_play(fix_text, on_ready=_on_ready) synth_and_play(fix_text, on_ready=_on_ready)
else: else:
# Re-gen the manifest audio for the review word # Re-gen the manifest audio for the review word
@ -528,18 +549,21 @@ class ProperNounAuditor(tk.Tk):
self.now_playing_var.set(f"… regen {word}") self.now_playing_var.set(f"… regen {word}")
def _regen(): def _regen():
import warnings, numpy as np try:
pipeline = _get_pipeline() import warnings, numpy as np
chunks = [] pipeline = _get_pipeline()
with warnings.catch_warnings(): chunks = []
warnings.filterwarnings("ignore", category=UserWarning) with warnings.catch_warnings():
for _, _, audio in pipeline(word, voice=VOICE): warnings.filterwarnings("ignore", category=UserWarning)
if audio is not None: for _, _, audio in pipeline(word, voice=VOICE):
chunks.append(audio) if audio is not None:
if chunks: chunks.append(audio)
sf.write(str(wav_path), np.concatenate(chunks), SAMPLE_RATE) if chunks:
self.after(0, lambda: self.now_playing_var.set(word)) sf.write(str(wav_path), np.concatenate(chunks), SAMPLE_RATE)
play_async(wav_path) self._safe_after(0, lambda: self.now_playing_var.set(word))
play_async(wav_path)
except Exception as exc:
print(f"[regen] error for '{word}': {exc}")
threading.Thread(target=_regen, daemon=True).start() threading.Thread(target=_regen, daemon=True).start()
@ -669,17 +693,21 @@ class ProperNounAuditor(tk.Tk):
self._pregen_status_var.set(f"0 / {new_count} new ({already} cached)") self._pregen_status_var.set(f"0 / {new_count} new ({already} cached)")
def _run(): def _run():
done = 0 try:
for rep in replacements: done = 0
cache_path = REPLACEMENTS_DIR / f"{_slug(rep)}.wav" for rep in replacements:
if not cache_path.exists(): cache_path = REPLACEMENTS_DIR / f"{_slug(rep)}.wav"
_synth_to_cache(rep) if not cache_path.exists():
done += 1 _synth_to_cache(rep)
self.after(0, lambda d=done, t=new_count: done += 1
self._pregen_status_var.set(f"{d} / {t} synthesised…")) self._safe_after(0, lambda d=done, t=new_count:
self.after(0, lambda: self._pregen_status_var.set( self._pregen_status_var.set(f"{d} / {t} synthesised…"))
f"Done — {total} clips ready")) self._safe_after(0, lambda: self._pregen_status_var.set(
self.after(0, lambda: self._pregen_btn.config(state="normal")) f"Done — {total} clips ready"))
except Exception as exc:
print(f"[pregen] error: {exc}")
finally:
self._safe_after(0, lambda: self._pregen_btn.config(state="normal"))
threading.Thread(target=_run, daemon=True).start() threading.Thread(target=_run, daemon=True).start()

View File

@ -1,15 +1,13 @@
[ [
"Ninety-Two",
"Gilgal",
"Nat", "Nat",
"Monoriah", "Monoriah",
"Akim", "Akim",
"Amoron", "Amoron",
"Migdan Idi",
"Migdan", "Migdan",
"Midgan-Idi", "Midgan-Idi",
"Midgan Idi",
"Midgan", "Midgan",
"Mic",
"Mi",
"Mentina", "Mentina",
"Hemeacum", "Hemeacum",
"Micah", "Micah",
@ -21,7 +19,6 @@
"Kishkumen", "Kishkumen",
"Kee", "Kee",
"Kayith", "Kayith",
"Kay",
"Pah", "Pah",
"Kamiakim", "Kamiakim",
"Corian-Co-Hah", "Corian-Co-Hah",
@ -29,9 +26,6 @@
"Chunish", "Chunish",
"Chu", "Chu",
"Cheem", "Cheem",
"Co",
"Thanksgiving",
"Way",
"Zoreth", "Zoreth",
"Zoramites", "Zoramites",
"Zoramite", "Zoramite",
@ -42,374 +36,120 @@
"Zen", "Zen",
"Zeezret", "Zeezret",
"Zedekiah", "Zedekiah",
"King",
"Zarahemla", "Zarahemla",
"Yourselves",
"Yourself",
"Yours",
"Kingdom",
"Day",
"Young",
"Yohks", "Yohks",
"Yesterday",
"Yay",
"Writings",
"Write",
"Worthy",
"Worlds",
"World",
"Worketh", "Worketh",
"Word",
"Women",
"Woman",
"Woe", "Woe",
"Wives",
"Winter",
"Winebag", "Winebag",
"Winding", "Winding",
"Wind",
"Willow", "Willow",
"Wife",
"Whom",
"Whereupon", "Whereupon",
"Wherefore", "Wherefore",
"Whatsoever",
"Western",
"West",
"Weeks",
"Wee",
"Ways",
"Waylit", "Waylit",
"Wayat", "Wayat",
"Waters",
"Water",
"Washing",
"Wards", "Wards",
"War",
"Wallohitwah", "Wallohitwah",
"Walk",
"Wah", "Wah",
"Voice",
"Virtue",
"Vineyards", "Vineyards",
"Vineyard", "Vineyard",
"Verily", "Verily",
"Veil", "Veil",
"Heaven",
"Valley",
"Urim", "Urim",
"Unquenchable", "Unquenchable",
"Fire",
"Universe",
"Universal",
"United",
"Two-Fold", "Two-Fold",
"Two",
"Thousand",
"Months",
"Hundred",
"Days",
"Twins",
"Twenty",
"Five",
"Years",
"Twelve",
"Tugo", "Tugo",
"Truth",
"True",
"Tree",
"Traveling",
"Councils",
"Peli", "Peli",
"Tornit", "Tornit",
"Torieth", "Torieth",
"Tor", "Tor",
"Toniah Lotnah",
"Toniah", "Toniah",
"Tomorrow",
"Token",
"Today",
"Tithe", "Tithe",
"Timothy", "Timothy",
"Son",
"Brethren", "Brethren",
"Thunder",
"Thummim", "Thummim",
"Three",
"Quarters",
"Thousands",
"Miles",
"Knewest", "Knewest",
"Perilous", "Perilous",
"Year",
"Present",
"Land",
"Northward", "Northward",
"Holy",
"Spirit",
"Place",
"Ghost",
"Great",
"Council",
"Everlasting", "Everlasting",
"Covenant", "Covenant",
"Thirteen",
"Third",
"Last",
"Men",
"Spirits",
"Firstborn", "Firstborn",
"River",
"Sea",
"Mountains",
"Anointing", "Anointing",
"Wasatch", "Wasatch",
"Front",
"Warm",
"Mob",
"Law",
"Restoration",
"Order",
"Sons",
"Daughters",
"Elohim", "Elohim",
"Heavenly", "Heavenly",
"People",
"Life",
"Knowledge",
"Good",
"Evil",
"Thirty",
"Eighth",
"Book",
"Lodge",
"Terrible",
"Terrestrial", "Terrestrial",
"Temple",
"Lord",
"Eve", "Eve",
"Bountiful", "Bountiful",
"Hill",
"Tamahu-Ah", "Tamahu-Ah",
"Sweat",
"Sure",
"Sign",
"Nail",
"Summer",
"Spring",
"Levi", "Levi",
"Father",
"Snake",
"Sixty",
"Ninth",
"Sixth",
"Shortest", "Shortest",
"Seventy",
"Fifth",
"Seventh",
"Sacred",
"Direction",
"Sees",
"Habitation", "Habitation",
"Seeks",
"Second",
"Endowment", "Endowment",
"Season",
"Coast",
"Salten", "Salten",
"Record",
"Pipe",
"Herbs", "Herbs",
"Hearth", "Hearth",
"Garments", "Garments",
"Directions",
"Animals",
"Sabbath", "Sabbath",
"Robe", "Robe",
"Priesthood", "Priesthood",
"Akish", "Akish",
"Right",
"Prayer",
"Remnant", "Remnant",
"House",
"Israel", "Israel",
"Jacob", "Jacob",
"Purification", "Purification",
"Ammonites", "Ammonites",
"Melchizedek", "Melchizedek",
"Power",
"Mother",
"God",
"Sacrifice",
"Adam", "Adam",
"Ceremony",
"Gulf",
"Hagoth", "Hagoth",
"Corianton", "Corianton",
"Ammon", "Ammon",
"Patriarchal", "Patriarchal",
"Grip",
"Path",
"Creator",
"Past",
"Ten",
"Orders",
"North",
"Country",
"Ninety", "Ninety",
"Night",
"Next",
"Newborn",
"Children",
"New",
"Nemenha", "Nemenha",
"Nem", "Nem",
"Volume",
"Another",
"Testament",
"Jesus",
"Christ",
"Prophet",
"Living",
"Morning",
"Middle",
"Archives",
"Medicine",
"Dance",
"Man",
"Higher",
"Lord'S", "Lord'S",
"Harvest",
"Looks",
"Light",
"Levitical", "Levitical",
"Laying",
"Hands",
"Laws",
"Gospel",
"Revelation",
"Obedience", "Obedience",
"Consecration", "Consecration",
"Chastity", "Chastity",
"Latter-Day", "Latter-Day",
"Latter",
"Lands",
"Southward", "Southward",
"Jerusalem",
"Desolation", "Desolation",
"Lake",
"Islands",
"Hour",
"Hosts",
"Host",
"City",
"High",
"Priest",
"Region",
"Guide",
"Star",
"Growing",
"Salt",
"Healer", "Healer",
"Peace",
"Gift",
"Healing",
"General",
"Nespelem", "Nespelem",
"Friends",
"Fourth",
"Four",
"Forty-Two", "Forty-Two",
"Fortieth", "Fortieth",
"Following",
"Sixty-Seventh", "Sixty-Seventh",
"First",
"Home",
"Pa",
"Firmament", "Firmament",
"Final",
"Festival",
"Lights",
"Fair",
"Ones",
"End",
"Elect",
"Eighty", "Eighty",
"Eastern",
"East",
"Earth",
"Dreadful", "Dreadful",
"Coriantumr", "Coriantumr",
"Savior", "Savior",
"Mayan", "Mayan",
"Elders", "Elders",
"Mothers",
"Community",
"Church",
"Certain",
"Common",
"Consent",
"Cold",
"Josh",
"Gad", "Gad",
"Enoch", "Enoch",
"Saints",
"Chiefs",
"Indians",
"Celestial", "Celestial",
"Glory",
"Canyons", "Canyons",
"Buffalo",
"Breath",
"Brass",
"Plates",
"Bowl",
"Incense", "Incense",
"Mormon", "Mormon",
"Ether", "Ether",
"Ancient",
"Quarter",
"Tenth",
"Tens",
"Tempter", "Tempter",
"Temnet", "Temnet",
"Telleth", "Telleth",
"Teemkt", "Teemkt",
"Tee",
"Tay", "Tay",
"Tarramarhah", "Tarramarhah",
"Tan",
"Tamahu", "Tamahu",
"Talking",
"Talk",
"Tah", "Tah",
"Sweetgrass", "Sweetgrass",
"Supposeth", "Supposeth",
"Supper", "Supper",
"Superior",
"Sun",
"Sufficeth", "Sufficeth",
"Subdue", "Subdue",
"Lucifer", "Lucifer",
"Stones",
"Stephat", "Stephat",
"Stars",
"Stakes",
"Spiritual",
"Spine",
"Speaketh", "Speaketh",
"Speaker",
"Sovereign",
"South",
"Time",
"Ago",
"Later",
"Date",
"Future",
"Sky",
"Sixteen",
"Age",
"Six",
"Sineth", "Sineth",
"Simeon", "Simeon",
"Sidon", "Sidon",
@ -429,81 +169,50 @@
"Shadowing", "Shadowing",
"Sha", "Sha",
"Sevim", "Sevim",
"Several",
"Hours",
"Seventeen",
"Seven",
"Setteth", "Setteth",
"Set",
"Session",
"Servant",
"Seer", "Seer",
"Seeketh", "Seeketh",
"Seek",
"Seat",
"Sealing", "Sealing",
"Saviors", "Saviors",
"Satan", "Satan",
"Sanith", "Sanith",
"Saneth", "Saneth",
"San",
"Stretch",
"Spake", "Spake",
"Preach", "Preach",
"Answered",
"Samuel", "Samuel",
"Samith", "Samith",
"Samal", "Samal",
"Salamander", "Salamander",
"Safety",
"Plants",
"Manner",
"Sacrament", "Sacrament",
"Sabel-Nah", "Sabel-Nah",
"Sabel", "Sabel",
"Sabbaths", "Sabbaths",
"Room",
"Robbers", "Robbers",
"Rhen", "Rhen",
"Resurrected", "Resurrected",
"Resume",
"Repent", "Repent",
"Remove",
"Rejoice", "Rejoice",
"Redeemer", "Redeemer",
"Recall",
"Queens",
"Prophets", "Prophets",
"Moroni", "Moroni",
"Prophecy", "Prophecy",
"Promise",
"Priests",
"Priestesses", "Priestesses",
"Priestess", "Priestess",
"President",
"Preacher", "Preacher",
"Prayeth", "Prayeth",
"Pray",
"Potal", "Potal",
"Porinor", "Porinor",
"Por", "Por",
"Plan",
"Plains",
"Places",
"Pingwit", "Pingwit",
"Pharaoh", "Pharaoh",
"Peter", "Peter",
"James", "James",
"John", "John",
"Person",
"Perisheth", "Perisheth",
"Altar",
"Penith", "Penith",
"Pel", "Pel",
"Pee",
"Peacemaker", "Peacemaker",
"Parim", "Parim",
"Parents",
"Parah", "Parah",
"Panith-Het", "Panith-Het",
"Panith", "Panith",
@ -517,101 +226,50 @@
"Hem", "Hem",
"Pagwit", "Pagwit",
"Pac-Sineth", "Pac-Sineth",
"Pac",
"Ordinance", "Ordinance",
"One",
"Forty",
"Half",
"Omega",
"Old",
"Ohmer", "Ohmer",
"Oh",
"Observe",
"Nu", "Nu",
"Notwithstanding", "Notwithstanding",
"Nomiah-Min", "Nomiah-Min",
"Nomiah", "Nomiah",
"Min",
"Noahs", "Noahs",
"Noah", "Noah",
"Ninety-Two Years",
"Nine",
"Nin-Shepa", "Nin-Shepa",
"Nin", "Nin",
"Week",
"Name",
"Hills",
"Nespelite", "Nespelite",
"Nay",
"Nathah", "Nathah",
"Nemenhah", "Nemenhah",
"Nemen", "Nemen",
"Neighbor",
"Nature",
"Natural",
"Narrator",
"Myself",
"Chosen",
"Mulekites", "Mulekites",
"Mulekite", "Mulekite",
"Mulek", "Mulek",
"Muel", "Muel",
"Moveth", "Moveth",
"Mouth",
"Mouse",
"Mountain",
"Mount",
"Fathers",
"Moses", "Moses",
"Morrow", "Morrow",
"Morin", "Morin",
"Moriantum", "Moriantum",
"Moon",
"Millions",
"Michael", "Michael",
"Methuselah", "Methuselah",
"Messengers", "Messengers",
"Melek", "Melek",
"Meet",
"Fall",
"Master",
"Manna", "Manna",
"Maker",
"Love",
"Lords",
"Look",
"Lone",
"Lodges", "Lodges",
"Lo",
"Lives",
"Limhi", "Limhi",
"Lightning",
"Levites", "Levites",
"Levite", "Levite",
"Lay",
"Lamb",
"Lakes",
"Korim", "Korim",
"Knoweth", "Knoweth",
"Know",
"Kinsman", "Kinsman",
"Kings",
"Mosiah", "Mosiah",
"Keen",
"Judgment",
"Judgeth", "Judgeth",
"Judge",
"Judah", "Judah",
"Joseph", "Joseph",
"Joram", "Joram",
"Revelator", "Revelator",
"Beloved",
"Jews",
"Himself",
"Jehovah", "Jehovah",
"Jaredites", "Jaredites",
"Jaredite", "Jaredite",
"Jared",
"Jaguar", "Jaguar",
"Jacobite", "Jacobite",
"Israelite", "Israelite",
@ -621,50 +279,26 @@
"Isaiah", "Isaiah",
"Isabel", "Isabel",
"Isaac", "Isaac",
"Instructor",
"Instruction",
"Inner",
"Indigenous",
"Americans",
"Husbands", "Husbands",
"Husband",
"Hundredth", "Hundredth",
"Hundreds",
"Hudson",
"Bay",
"Howbeit", "Howbeit",
"Hosanna", "Hosanna",
"Hopeth", "Hopeth",
"Child",
"Holiness", "Holiness",
"Hold",
"Hin", "Hin",
"Himneth", "Himneth",
"Hez", "Hez",
"Heth", "Heth",
"Het", "Het",
"Heroes",
"Hero",
"Heirs", "Heirs",
"Hebrew",
"Heavens", "Heavens",
"Beings",
"Heart",
"Hearken", "Hearken",
"Healers", "Healers",
"Head",
"Hay",
"Harlot", "Harlot",
"Ham", "Ham",
"Hemen", "Hemen",
"Guides",
"Guardian",
"Groweth", "Groweth",
"Grandmother",
"Grandfathers", "Grandfathers",
"Grandfather",
"Governor",
"Gods",
"Godliness", "Godliness",
"Godhead", "Godhead",
"Gideon", "Gideon",
@ -672,51 +306,28 @@
"Gentile", "Gentile",
"Gee", "Gee",
"Gaudy", "Gaudy",
"Gate",
"Garden",
"Fourteen",
"Forty-Nine", "Forty-Nine",
"Forgetfulness", "Forgetfulness",
"Minutes",
"Fifty",
"Finisher", "Finisher",
"Findeth", "Findeth",
"Figure",
"Feather", "Feather",
"Farewell",
"Falleth", "Falleth",
"Faith",
"Exalted", "Exalted",
"Being",
"Exaltation", "Exaltation",
"Evening",
"Eternal",
"Ephraim", "Ephraim",
"Envieth", "Envieth",
"English",
"Else",
"Eleven",
"Election",
"Eight",
"Egyptus", "Egyptus",
"Egyptian",
"Egypt", "Egypt",
"Eden", "Eden",
"Earlier",
"Eagle",
"Dogwood", "Dogwood",
"Doeth", "Doeth",
"Disciples", "Disciples",
"Disciple", "Disciple",
"Discernment", "Discernment",
"Devil",
"Desolate", "Desolate",
"Desireth", "Desireth",
"Depart", "Depart",
"Deep",
"Deaths",
"Days'", "Days'",
"Short",
"Aaron", "Aaron",
"Abel", "Abel",
"Abraham", "Abraham",
@ -724,65 +335,28 @@
"Adamant", "Adamant",
"Adversary", "Adversary",
"Afar", "Afar",
"Afternoon",
"Alpha",
"America",
"Amulek", "Amulek",
"Appointed",
"Angel",
"Angels",
"Anith", "Anith",
"Anointed", "Anointed",
"Anti",
"Apostle", "Apostle",
"Arise",
"Arm",
"Little",
"Least",
"Atonement", "Atonement",
"Availeth", "Availeth",
"Babylon", "Babylon",
"Baptism", "Baptism",
"Baptist",
"Begin",
"Begotten", "Begotten",
"Belay", "Belay",
"Believeth", "Believeth",
"Bend",
"Benjamites", "Benjamites",
"Big",
"Blessed",
"Blessing",
"Body",
"Break",
"Brigham", "Brigham",
"Bringeth", "Bringeth",
"Meekness", "Meekness",
"Brother",
"Bush",
"Cain", "Cain",
"Calling",
"Captain",
"Cedar", "Cedar",
"Celebration",
"Celebrations",
"Chapters",
"Chief",
"Churches",
"Cities",
"Laman", "Laman",
"Claimeth", "Claimeth",
"Clan",
"Coasts", "Coasts",
"Come",
"Comforter", "Comforter",
"Condemn", "Condemn",
"Corian", "Corian",
"Cosmos", "Cosmos"
"Counsel",
"County",
"Creation",
"Cried",
"Daily",
"Dead"
] ]

View File

@ -1,11 +1,8 @@
{ {
"Gadianton Robbers": "Gadeeantun Robbers",
"Gadianton": "Gadeeantun", "Gadianton": "Gadeeantun",
"Coriantumr": "Coryantomer", "Coriantumr": "Coryantomer",
"Laman": "Layman", "Laman": "Layman",
"Lehi And Nephi": "Leehi And Nephi",
"Lehi": "Leehi", "Lehi": "Leehi",
"Lehi Mathonihah": "Leehi Mathonihah",
"Lehis": "Leehis", "Lehis": "Leehis",
"Lehies": "Leehis", "Lehies": "Leehis",
"Liahona": "Leeahona", "Liahona": "Leeahona",
@ -14,15 +11,14 @@
"Gadiantons": "Gadeeantuns", "Gadiantons": "Gadeeantuns",
"Laban": "Layban", "Laban": "Layban",
"Mosiah": "Moziah", "Mosiah": "Moziah",
"Mosiah The King": "Moziah The King",
"Nehors": "Kneehores", "Nehors": "Kneehores",
"Samuel The Lamanite": "Samuel The Laymanite",
"Tarry": "Tarery", "Tarry": "Tarery",
"The Lamanite Twins": "The Laymanite Twins", "Nephihah": "Kneefihah",
"The Lamanites Of Ammon": "The Laymanites Of Ammon", "Nephihet": "Kneefihet",
"The Lamanites Of The Land Of Zarahemla": "The Laymanites Of The Land Of Zarahemla", "Nephite": "Kneefite",
"The Lamanites Of The Land Southward": "The Laymanites Of The Land Southward", "Nephites": "Kneefites",
"The Lamanites Of The People Of Ammon": "The Laymanites Of The People Of Ammon", "Nephi-Im": "Kneefi-Im",
"The Lamb'S Book Of Life": "The Lamb's Book Of Life", "Nephitish": "Kneefitish",
"The Land Of Nephi": "The Land Of Kneefi" "Zenephi": "Zekneefi",
"Nephi": "Kneefi"
} }