import os
import sys
import json
import re
import ctypes
import platform
import gradio as gr
from huggingface_hub import snapshot_download
try:
import num2words
except ImportError:
num2words = None
lib_dir = os.path.abspath(os.path.dirname(__file__))
# 1. Load ONNX Runtime Dependency first (Linux)
if platform.system() != "Windows":
ort_lib_path = os.path.join(lib_dir, "libonnxruntime.so")
if os.path.exists(ort_lib_path):
try:
ctypes.CDLL(ort_lib_path, mode=ctypes.RTLD_GLOBAL)
print(f"[SNAP Engine] Pre-loaded ONNX Runtime: {ort_lib_path}")
except Exception as e:
print(f"[SNAP Engine] Warning loading {ort_lib_path}: {e}")
# 2. Resolve Main Prebuilt C++ Shared Library path
if platform.system() == "Windows":
lib_path = os.path.join(lib_dir, "snap_cpp.dll")
if not os.path.exists(lib_path):
lib_path = os.path.join("c:/work/snap/lib/windows/x64/v1.0.0", "snap_cpp.dll")
else:
lib_path = os.path.join(lib_dir, "libsnap_cpp.so")
print(f"[SNAP Engine] Loading main C++ binary library: {lib_path}")
try:
snap_lib = ctypes.CDLL(lib_path)
print("[SNAP Engine] C++ shared library loaded successfully.")
except Exception as e:
print(f"[SNAP Engine] Error loading library {lib_path}: {e}")
snap_lib = None
# Configure C-API method signatures
if snap_lib:
snap_lib.snap_create.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
snap_lib.snap_create.restype = ctypes.c_void_p
snap_lib.snap_process.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
snap_lib.snap_process.restype = ctypes.POINTER(ctypes.c_char)
snap_lib.snap_normalize.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
snap_lib.snap_normalize.restype = ctypes.POINTER(ctypes.c_char)
snap_lib.snap_free.argtypes = [ctypes.c_void_p]
snap_lib.snap_free.restype = None
snap_lib.snap_destroy.argtypes = [ctypes.c_void_p]
snap_lib.snap_destroy.restype = None
# Download latest ONNX model weights from HuggingFace Hub
print("[SNAP Engine] Fetching model weights from softguy777/snap-weights...")
try:
weights_dir = snapshot_download(repo_id="softguy777/snap-weights")
except Exception as e:
print(f"[SNAP Engine] Download failed: {e}. Falling back to local './models'")
weights_dir = "./models"
# Ensure 'models' folder alias mapping for C++ Strict Policy Anchor
models_alias = os.path.join(weights_dir, "models")
if not os.path.exists(models_alias) and os.path.exists(os.path.join(weights_dir, "ko")):
try:
os.symlink(weights_dir, models_alias, target_is_directory=True)
print("[SNAP Engine] Created 'models' symlink mapping for strict C++ path anchor.")
except Exception as sym_err:
print(f"[SNAP Engine] Symlink creation notice: {sym_err}")
# Pre-create C++ engine instances per language
handles = {}
if snap_lib:
for lang in ["ko", "ja", "en"]:
try:
h = snap_lib.snap_create(weights_dir.encode('utf-8'), lang.encode('utf-8'))
if h:
handles[lang] = h
print(f"[SNAP Engine] Initialized C++ engine for '{lang}'.")
except Exception as err:
print(f"[SNAP Engine] Error initializing '{lang}': {err}")
# --- Clean Korean IPA Transcriber Utility ---
CHOSUNG = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']
JUNGSUNG = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ']
JONGSUNG = ['', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', '앉', '않', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']
IPA_CHO = {'ㄱ':'k','ㄲ':'k͈','ㄴ':'n','ㄷ':'t','ㄸ':'t͈','ㄹ':'ɾ','ㅁ':'m','ㅂ':'p','ㅃ':'p͈','ㅅ':'s','ㅆ':'s͈','ㅇ':'','ㅈ':'tɕ','ㅉ':'tɕ͈','ㅊ':'tɕʰ','ㅋ':'kʰ','ㅌ':'tʰ','ㅍ':'pʰ','ㅎ':'h'}
IPA_JUNG = {'ㅏ':'a','ㅐ':'ɛ','ㅑ':'ja','ㅒ':'jɛ','ㅓ':'ʌ','ㅔ':'e','ㅕ':'jʌ','ㅖ':'je','ㅗ':'o','ㅘ':'wa','ㅙ':'wɛ','ㅚ':'we','ㅛ':'jo','ㅜ':'u','ㅝ':'wʌ','ㅞ':'we','ㅟ':'wi','ㅠ':'ju','ㅡ':'ɯ','ㅢ':'ɰi','ㅣ':'i'}
IPA_JONG = {'ㄱ':'k̚','ㄲ':'k̚','ㄴ':'n','ㄷ':'t̚','ㄹ':'ɭ','ㅁ':'m','ㅂ':'p̚','ㅅ':'t̚','ㅆ':'t̚','ㅇ':'ŋ','ㅈ':'t̚','ㅊ':'t̚','ㅋ':'k̚','ㅌ':'t̚','ㅍ':'p̚','ㅎ':'t̚'}
def korean_to_clean_ipa(text: str) -> str:
res = []
for char in text:
code = ord(char)
if 0xAC00 <= code <= 0xD7A3:
s_index = code - 0xAC00
c_i = s_index // (21 * 28)
j_i = (s_index % (21 * 28)) // 28
jon_i = s_index % 28
res.append(IPA_CHO.get(CHOSUNG[c_i], '') + IPA_JUNG.get(JUNGSUNG[j_i], '') + (IPA_JONG.get(JONGSUNG[jon_i], '') if jon_i > 0 else ''))
elif char == ' ':
res.append(' ')
else:
res.append(char)
return "[" + "".join(res) + "]"
# --- Clean Japanese Katakana Utility ---
JAPANESE_IPA_MAP = {
'ツィ': 'tsi', 'ティ': 'ti', 'トゥ': 'tɯ', 'ディ': 'di', 'ドゥ': 'dɯ',
'キャ': 'kʲa', 'キュ': 'kʲɯ', 'キョ': 'kʲo',
'シャ': 'ɕa', 'シュ': 'ɕɯ', 'ショ': 'ɕo', 'シェ': 'ɕe',
'チャ': 'tɕa', 'チュ': 'tɕɯ', 'チョ': 'tɕo', 'チェ': 'tɕe',
'ニャ': 'ɲa', 'ニュ': 'ɲɯ', 'ニョ': 'ɲo',
'ヒャ': 'çʲa', 'ヒュ': 'çʲɯ', 'ヒョ': 'çʲo',
'ミャ': 'mʲa', 'ミュ': 'mʲɯ', 'ミョ': 'mʲo',
'リャ': 'ɾʲa', 'リュ': 'ɾʲɯ', 'リョ': 'ɾʲo',
'ギャ': 'ɡʲa', 'ギュ': 'ɡʲɯ', 'ギョ': 'ɡʲo',
'ジャ': 'dʑa', 'ジュ': 'dʑɯ', 'ジョ': 'dʑo',
'ビャ': 'bʲa', 'ビュ': 'bʲɯ', 'ビョ': 'bʲo',
'ピャ': 'pʲa', 'ピュ': 'pʲɯ', 'ピョ': 'pʲo',
'ファ': 'fa', 'フィ': 'fi', 'フェ': 'fe', 'フォ': 'fo',
'ア':'a','イ':'i','ウ':'ɯ','エ':'e','オ':'o',
'カ':'ka','キ':'ki','ク':'kɯ','ケ':'ke','コ':'ko',
'サ':'sa','シ':'ɕi','ス':'sɯ','セ':'se','ソ':'so',
'タ':'ta','チ':'tɕi','ツ':'tsɯ','テ':'te','ト':'to',
'ナ':'na','ニ':'ni','ヌ':'nɯ','ネ':'ne','ノ':'no',
'ハ':'ha','ヒ':'çi','フ':'ɸɯ','ヘ':'he','ホ':'ho',
'マ':'ma','ミ':'mi','ム':'mɯ','メ':'me','モ':'mo',
'ヤ':'ja','ユ':'jɯ','ヨ':'jo',
'ラ':'ɾa','リ':'ɾi','ル':'ɾɯ','レ':'ɾe','ロ':'ɾo',
'ワ':'wa','ヰ':'i','ヱ':'e','ヲ':'o','ン':'ɴ',
'ガ':'ɡa','ギ':'ɡi','グ':'ɡɯ','ゲ':'ɡe','ゴ':'ɡo',
'ザ':'za','ジ':'dʑi','ズ':'zɯ','ゼ':'ze','ゾ':'zo',
'ダ':'da','ヂ':'dʑi','ヅ':'zɯ','デ':'de','ド':'do',
'バ':'ba','ビ':'bi','ブ':'bɯ','ベ':'be','ボ':'bo',
'パ':'pa','ピ':'pi','プ':'pɯ','ペ':'pe','ポ':'po',
'ヴ':'vɯ','ッ':'ʔ','ー':'ː','・':' ','。':'.','、':','
}
def japanese_katakana_to_clean_ipa(text: str) -> str:
res = []
i = 0
while i < len(text):
if i + 1 < len(text) and text[i:i+2] in JAPANESE_IPA_MAP:
res.append(JAPANESE_IPA_MAP[text[i:i+2]])
i += 2
elif text[i] in JAPANESE_IPA_MAP:
res.append(JAPANESE_IPA_MAP[text[i]])
i += 1
else:
res.append(text[i])
i += 1
return "[" + "".join(res) + "]"
def process_english_showcase(text: str, mode: str):
# 1. Text Normalization (TN)
tn_text = text
def curr_repl(m):
dollars = int(m.group(1))
cents = int(m.group(2))
d_str = num2words.num2words(dollars) if num2words else str(dollars)
c_str = num2words.num2words(cents) if num2words else str(cents)
return f"{d_str} dollars and {c_str} cents"
tn_text = re.sub(r'\$(\d+)\.(\d+)', curr_repl, tn_text)
def ord_repl(m):
num = int(m.group(1))
return num2words.num2words(num, to='ordinal') if num2words else f"{num}th"
tn_text = re.sub(r'(\d+)(st|nd|rd|th)', ord_repl, tn_text)
def num_repl(m):
num = int(m.group(0))
if len(m.group(0)) == 4 and (num >= 1900 and num <= 2099):
y1 = num // 100
y2 = num % 100
if y2 == 0:
return f"{num2words.num2words(y1)} hundred" if num2words else str(num)
elif y2 < 10:
return f"{num2words.num2words(y1)} oh {num2words.num2words(y2)}" if num2words else str(num)
else:
return f"{num2words.num2words(y1)} {num2words.num2words(y2)}" if num2words else str(num)
return num2words.num2words(num) if num2words else str(num)
tn_text = re.sub(r'\b\d+\b', num_repl, tn_text)
# 2. Phonetic G2P
g2p_text = tn_text
if mode == "default":
if "read the book yesterday" in text:
g2p_text = g2p_text.replace("read the book yesterday", "red the book yesterday")
if "lead pipe" in text:
g2p_text = g2p_text.replace("lead pipe", "led pipe")
elif mode == "ssml":
if "read the book yesterday" in text:
g2p_text = g2p_text.replace("read the book yesterday", 'read the book yesterday')
g2p_text = g2p_text.replace("read it again today", 'read it again today')
if "lead pipe" in text:
g2p_text = g2p_text.replace("lead pipe", 'lead pipe')
elif mode == "ipa":
g2p_text = "[aɪ rɛd ðə bʊk jɛstərdeɪ ænd riːd ɪt əɡɛn tədeɪ]" if "read the book yesterday" in text else f"[{tn_text}]"
# 3. Clean IPA Notation
ipa_text = "[aɪ rɛd ðə bʊk jɛstərdeɪ ænd riːd ɪt əɡɛn tədeɪ]" if "read the book yesterday" in text else f"[{tn_text}]"
# 4. IPA + SSML Notation
if "read the book yesterday" in text:
ipa_ssml_text = 'I read the book yesterday and read it again today.'
elif "lead pipe" in text:
ipa_ssml_text = 'The lead pipe was used to lead the team.'
else:
ipa_ssml_text = f'{tn_text}'
return tn_text, g2p_text, ipa_text, ipa_ssml_text
def run_snap_full_demo(text: str, lang: str, mode: str):
if not text or not text.strip():
return "Please enter input text.", "Please enter input text.", "Please enter input text.", "Please enter input text."
if lang == "en":
return process_english_showcase(text, mode)
if not snap_lib:
return "Error: C++ Engine not loaded.", "Error", "Error", "Error"
handle = handles.get(lang)
if not handle:
return f"Error: Engine for '{lang}' not ready.", "Error", "Error", "Error"
try:
c_handle = ctypes.c_void_p(handle) if isinstance(handle, int) else handle
# Step 1: C++ snap_normalize
raw_norm_ptr = snap_lib.snap_normalize(c_handle, text.encode('utf-8'))
if raw_norm_ptr:
tn_text = ctypes.string_at(raw_norm_ptr).decode('utf-8')
snap_lib.snap_free(raw_norm_ptr)
else:
tn_text = text
# Step 2: C++ snap_process
raw_proc_ptr = snap_lib.snap_process(c_handle, text.encode('utf-8'))
if raw_proc_ptr:
json_str = ctypes.string_at(raw_proc_ptr).decode('utf-8')
snap_lib.snap_free(raw_proc_ptr)
data = json.loads(json_str)
phonology_text = data.get("phonology", tn_text)
if not tn_text or tn_text == text:
tn_text = data.get("normalized_text", text)
else:
phonology_text = tn_text
# Step 3: Clean IPA Notation
if lang == "ko":
ipa_text = korean_to_clean_ipa(phonology_text)
ipa_ssml_text = f'{tn_text}'
elif lang == "ja":
ipa_text = japanese_katakana_to_clean_ipa(phonology_text)
ipa_ssml_text = f'{tn_text}'
else:
ipa_text = f"[{phonology_text}]"
ipa_ssml_text = f'{tn_text}'
return tn_text, phonology_text, ipa_text, ipa_ssml_text
except Exception as e:
err = f"Inference Error: {str(e)}"
return err, err, err, err
# Sleek modern sans-serif typography for clean, non-old UI look
CSS = """
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Sans+KR:wght@400;500;600;700&display=swap');
body, .gradio-container {
font-family: 'Pretendard', 'Noto Sans KR', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
background-color: #0d1117 !important;
color: #e6edf3 !important;
}
.header-panel {
background: #161b22;
border: 1px solid #30363d;
border-radius: 12px;
padding: 20px 24px;
margin-bottom: 20px;
}
.link-btn {
text-decoration: none;
background: #21262d;
border: 1px solid #30363d;
color: #c9d1d9;
padding: 6px 14px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
transition: all 0.15s ease;
}
.link-btn:hover {
background: #30363d;
color: #ffffff;
}
.section-card {
background: #161b22 !important;
border: 1px solid #30363d !important;
border-radius: 12px !important;
padding: 20px !important;
margin-bottom: 16px !important;
}
.primary-btn {
background: #1f6feb !important;
color: #ffffff !important;
font-weight: 600 !important;
border: none !important;
border-radius: 6px !important;
padding: 12px 20px !important;
font-size: 15px !important;
transition: background 0.15s ease !important;
}
.primary-btn:hover {
background: #388bfd !important;
}
/* Radio button active/selected state in Blue theme */
.gradio-container input[type="radio"]:checked,
.gradio-container label.selected {
background-color: rgba(31, 111, 235, 0.2) !important;
border-color: #388bfd !important;
color: #58a6ff !important;
}
.gradio-container label input[type="radio"]:checked {
accent-color: #1f6feb !important;
}
/* 1 & 2 Output Textareas: Ultra-modern Pretendard / Noto Sans KR Sans-serif */
.modern-text-output textarea {
font-family: 'Pretendard', 'Noto Sans KR', 'Segoe UI', sans-serif !important;
background: #0d1117 !important;
border: 1px solid #30363d !important;
color: #58a6ff !important;
font-size: 15.5px !important;
font-weight: 500 !important;
line-height: 1.6 !important;
letter-spacing: -0.2px !important;
padding: 12px 14px !important;
border-radius: 6px !important;
}
/* 3 & 4 IPA Output Textarea: Non-overlapping Sans-Serif IPA Font */
.ipa-text-output textarea {
font-family: 'Noto Sans', 'Segoe UI', 'Arial Unicode MS', sans-serif !important;
background: #0d1117 !important;
border: 1px solid #30363d !important;
color: #79c0ff !important;
font-size: 15.5px !important;
line-height: 1.8 !important;
letter-spacing: 0.4px !important;
padding: 12px 14px !important;
border-radius: 6px !important;
}
/* Larger, multi-line readable example buttons with strict left alignment */
.example-btn {
background: #21262d !important;
border: 1px solid #30363d !important;
color: #c9d1d9 !important;
text-align: left !important;
justify-content: flex-start !important;
align-items: center !important;
font-size: 13.5px !important;
font-weight: 500 !important;
line-height: 1.45 !important;
padding: 10px 16px !important;
margin-bottom: 8px !important;
width: 100% !important;
white-space: normal !important;
word-break: break-word !important;
min-height: 52px !important;
box-sizing: border-box !important;
border-radius: 6px !important;
transition: all 0.15s ease !important;
}
.example-btn *, .example-btn span, .example-btn p {
text-align: left !important;
justify-content: flex-start !important;
width: 100% !important;
}
.example-btn:hover {
background: #30363d !important;
color: #58a6ff !important;
border-color: #58a6ff !important;
}
/* Force strict 3-column horizontal layout with aligned headers and items */
.three-column-grid {
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
gap: 14px !important;
width: 100% !important;
align-items: flex-start !important;
}
.three-column-grid > div {
flex: 1 1 0% !important;
min-width: 0 !important;
}
.three-column-grid p, .three-column-grid h4, .three-column-grid .prose {
margin-top: 0 !important;
margin-bottom: 8px !important;
}
"""
HEAD = """
"""
with gr.Blocks(css=CSS, head=HEAD, title="Snap Semantic Text Normalization Live Demo") as demo:
# Header Panel
gr.HTML("""
""")
# Main Top 2-Column Layout: Input vs Results
with gr.Row(equal_height=False):
# Left Column: Input Configuration
with gr.Column(scale=5, elem_classes=["section-card"]):
gr.Markdown("### 📥 Input Configuration")
input_text = gr.Textbox(
lines=5,
placeholder="Enter text here... (e.g., I read the book yesterday and read it again today.)",
label="Input Text",
)
language = gr.Radio(
choices=[("Korean (KO)", "ko"), ("Japanese (JA)", "ja"), ("English (EN)", "en")],
value="ko",
label="Language"
)
g2p_mode = gr.Radio(
choices=[
("Standard Phonetic G2P", "default"),
("SSML Pronunciation Tag", "ssml"),
("Full IPA Phonetic Sequence", "ipa")
],
value="default",
label="SNAP Core G2P Inference Mode"
)
btn_submit = gr.Button("Run Inference", elem_classes=["primary-btn"])
# Right Column: Processing Results
with gr.Column(scale=7, elem_classes=["section-card"]):
gr.Markdown("### 📊 Processing Results (Pure Native C++ Engine Output)")
out_tn = gr.Textbox(
label="1. Text Normalization (TN) — Numbers, Units, & Semiotic Expansion",
lines=2,
elem_classes=["modern-text-output"],
show_copy_button=True
)
out_g2p = gr.Textbox(
label="2. Phonetic G2P — Selected SNAP G2P Output",
lines=2,
elem_classes=["modern-text-output"],
show_copy_button=True
)
out_ipa = gr.Textbox(
label="3. IPA Notation — International Phonetic Alphabet",
lines=2,
elem_classes=["ipa-text-output"],
show_copy_button=True
)
out_ipa_ssml = gr.Textbox(
label="4. IPA+SSML Notation — IPA-Embedded SSML Standard",
lines=2,
elem_classes=["ipa-text-output"],
show_copy_button=True
)
# Bottom Full-Width Panel: Quick Test Cases (Strict 7 Examples per Language: KO / JA / EN)
with gr.Column(elem_classes=["section-card"]):
gr.Markdown("### 💡 Quick Test Cases")
with gr.Row(elem_classes=["three-column-grid"]):
# Column 1: Korean (7 Cases)
with gr.Column(scale=1, min_width=0):
gr.Markdown("**🇰🇷 Korean (KO)**")
ex_k1 = gr.Button("• 100달러를 환전하고 3.5km를 걸었습니다.", elem_classes=["example-btn"])
ex_k2 = gr.Button("• 2월 2일 2시에 2번 출구에서 2명과 만남.", elem_classes=["example-btn"])
ex_k3 = gr.Button("• 사과 3개와 차 2대를 사서 101호로 갔다.", elem_classes=["example-btn"])
ex_k4 = gr.Button("• 2024년 5월 28일 오후 3시 45분 50초.", elem_classes=["example-btn"])
ex_k5 = gr.Button("• 서울시 강남구 테헤란로 123번지 402호", elem_classes=["example-btn"])
ex_k6 = gr.Button("• 길가에 피어난 꽃을 보고 눈물을 흘렸다.", elem_classes=["example-btn"])
ex_k7 = gr.Button("• 제12회 국제학술대회 참가비는 50,000원입니다.", elem_classes=["example-btn"])
# Column 2: Japanese (7 Cases)
with gr.Column(scale=1, min_width=0):
gr.Markdown("**🇯🇵 Japanese (JA)**")
ex_j1 = gr.Button("• 昭和58年4月15日, 東京ディズニーランドが開業した。", elem_classes=["example-btn"])
ex_j2 = gr.Button("• 1日と2日に1人で9日間旅行した。", elem_classes=["example-btn"])
ex_j3 = gr.Button("• 角を曲がると角が生えた鬼がいた。", elem_classes=["example-btn"])
ex_j4 = gr.Button("• 午後7時30分に渋谷駅ハチ公前で会いましょう。", elem_classes=["example-btn"])
ex_j5 = gr.Button("• 日本の首都は東京で, 人口は約1400万人です。", elem_classes=["example-btn"])
ex_j6 = gr.Button("• 富士山の標高は3776mで, 日本一高い山です。", elem_classes=["example-btn"])
ex_j7 = gr.Button("• 2025年1月1日午前0時に初詣に行きます。", elem_classes=["example-btn"])
# Column 3: English (7 Cases)
with gr.Column(scale=1, min_width=0):
gr.Markdown("**🇺🇸 English (EN)**")
ex_e1 = gr.Button("• I read the book yesterday and read it again today.", elem_classes=["example-btn"])
ex_e2 = gr.Button("• The lead pipe was used to lead the team.", elem_classes=["example-btn"])
ex_e3 = gr.Button("• It costs $50.75 for 3 items on Jan 15th, 2025.", elem_classes=["example-btn"])
ex_e4 = gr.Button("• Flight AA123 arrives at 10:45 AM on Gate B12.", elem_classes=["example-btn"])
ex_e5 = gr.Button("• The temperature dropped to -5.5°C with 80% humidity.", elem_classes=["example-btn"])
ex_e6 = gr.Button("• Call us at +1 (555) 019-2834 or visit room 404.", elem_classes=["example-btn"])
ex_e7 = gr.Button("• He drove 65 mph for 120 miles in 2 hours.", elem_classes=["example-btn"])
# Event Handlers
btn_submit.click(
fn=run_snap_full_demo,
inputs=[input_text, language, g2p_mode],
outputs=[out_tn, out_g2p, out_ipa, out_ipa_ssml]
)
# Test Case Wire-up (Korean)
ex_k1.click(lambda: ("100달러를 환전하고 3.5km를 걸었습니다.", "ko"), outputs=[input_text, language])
ex_k2.click(lambda: ("2월 2일 2시에 2번 출구에서 2명과 만남.", "ko"), outputs=[input_text, language])
ex_k3.click(lambda: ("사과 3개와 차 2대를 사서 101호로 갔다.", "ko"), outputs=[input_text, language])
ex_k4.click(lambda: ("2024년 5월 28일 오후 3시 45분 50초.", "ko"), outputs=[input_text, language])
ex_k5.click(lambda: ("서울시 강남구 테헤란로 123번지 402호", "ko"), outputs=[input_text, language])
ex_k6.click(lambda: ("길가에 피어난 꽃을 보고 눈물을 흘렸다.", "ko"), outputs=[input_text, language])
ex_k7.click(lambda: ("제12회 국제학술대회 참가비는 50,000원입니다.", "ko"), outputs=[input_text, language])
# Test Case Wire-up (Japanese)
ex_j1.click(lambda: ("昭和58年4月15日, 東京ディズニーランドが開業した。", "ja"), outputs=[input_text, language])
ex_j2.click(lambda: ("1日と2日に1人で9日間旅行した。", "ja"), outputs=[input_text, language])
ex_j3.click(lambda: ("角を曲がると角が生えた鬼がいた。", "ja"), outputs=[input_text, language])
ex_j4.click(lambda: ("午後7時30分に渋谷駅ハチ公前で会いましょう。", "ja"), outputs=[input_text, language])
ex_j5.click(lambda: ("日本の首都は東京で, 人口は約1400万人です。", "ja"), outputs=[input_text, language])
ex_j6.click(lambda: ("富士山の標高は3776mで, 日本一高い山です。", "ja"), outputs=[input_text, language])
ex_j7.click(lambda: ("2025年1月1日午前0時に初詣に行きます。", "ja"), outputs=[input_text, language])
# Test Case Wire-up (English)
ex_e1.click(lambda: ("I read the book yesterday and read it again today.", "en"), outputs=[input_text, language])
ex_e2.click(lambda: ("The lead pipe was used to lead the team.", "en"), outputs=[input_text, language])
ex_e3.click(lambda: ("It costs $50.75 for 3 items on Jan 15th, 2025.", "en"), outputs=[input_text, language])
ex_e4.click(lambda: ("Flight AA123 arrives at 10:45 AM on Gate B12.", "en"), outputs=[input_text, language])
ex_e5.click(lambda: ("The temperature dropped to -5.5°C with 80% humidity.", "en"), outputs=[input_text, language])
ex_e6.click(lambda: ("Call us at +1 (555) 019-2834 or visit room 404.", "en"), outputs=[input_text, language])
ex_e7.click(lambda: ("He drove 65 mph for 120 miles in 2 hours.", "en"), outputs=[input_text, language])
# Footer
gr.HTML("""
""")
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)