dseditor commited on
Commit
f4d5302
·
verified ·
1 Parent(s): 7bca07a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +372 -74
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Breeze2-VITS 繁體中文語音合成 - 增強
3
- 支援英文數字自動轉換為中文發音
4
  """
5
 
6
  import gradio as gr
@@ -24,11 +24,12 @@ except ImportError:
24
 
25
 
26
  class TextConverter:
27
- """文本轉換器,將英文和數字轉換為中文發音"""
28
 
29
  def __init__(self, mapping_file="text_mapping.txt"):
30
  self.mapping_file = Path(mapping_file)
31
  self.conversion_map = {}
 
32
  self.load_mapping()
33
 
34
  def load_mapping(self):
@@ -49,66 +50,154 @@ class TextConverter:
49
  self.conversion_map[original.strip().lower()] = chinese.strip()
50
 
51
  print(f"✅ 載入 {len(self.conversion_map)} 個轉換規則")
 
 
 
 
 
 
 
 
 
52
  else:
53
  print(f"⚠️ 轉換對照表文件不存在: {self.mapping_file}")
54
- self.create_default_mapping()
55
  except Exception as e:
56
  print(f"❌ 載入轉換對照表失敗: {e}")
57
- self.create_default_mapping()
58
 
59
- def create_default_mapping(self):
60
- """創建預設的轉換對照表"""
61
  default_mappings = {
62
  # 數字
63
  '0': '零', '1': '一', '2': '二', '3': '三', '4': '四',
64
  '5': '五', '6': '六', '7': '七', '8': '八', '9': '九',
65
- '10': '十', '100': '一', '1000': '一千',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- # 常用英文
68
- 'hello': '哈囉', 'hi': '', 'bye': '拜拜', 'ok': '歐凱',
69
- 'yes': '是的', 'no': '', 'good': '好的', 'bad': '不好',
 
 
70
 
71
  # 技術詞彙
72
- 'ai': '人工智慧', 'api': '程式介面', 'app': '應用程式',
73
- 'cpu': '中央處理器', 'gpu': '圖形處理器',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- # 字母
76
- 'a': '', 'b': '', 'c': '西', 'd': '迪', 'e': '伊'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
 
79
  self.conversion_map = default_mappings
80
- print(f"✅ 使用預設轉換規則: {len(default_mappings)} 個")
 
 
 
 
 
81
 
82
  def convert_numbers(self, text):
83
- """轉換連續數字為中文"""
 
 
84
  def number_to_chinese(match):
85
  number = match.group()
86
- if len(number) <= 2: # 簡單數字直接對應
 
 
87
  result = ""
88
  for digit in number:
89
- result += self.conversion_map.get(digit, digit)
 
 
90
  return result
91
  else:
92
  # 複雜數字處理
93
- return self.convert_large_number(number)
 
 
94
 
95
  # 匹配連續數字
96
- text = re.sub(r'\d+', number_to_chinese, text)
97
- return text
 
 
98
 
99
  def convert_large_number(self, number_str):
100
- """轉換大數字為中文"""
101
  try:
102
  num = int(number_str)
103
  if num == 0:
104
  return '零'
105
 
 
 
 
 
106
  # 簡化的數字轉換(支援到萬)
107
- units = ['', '十', '百', '千', '萬']
108
  digits = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
109
 
110
  if num < 10:
111
  return digits[num]
 
 
 
 
 
112
  elif num < 100:
113
  tens = num // 10
114
  ones = num % 10
@@ -123,48 +212,123 @@ class TextConverter:
123
  if remainder > 0:
124
  if remainder < 10:
125
  result += '零' + digits[remainder]
 
 
 
 
 
 
 
 
 
 
 
 
126
  else:
127
  result += self.convert_large_number(str(remainder))
128
  return result
129
  else:
130
- # 對於更大的數字,簡化處理
131
- return number_str # 保持原樣
 
 
 
132
  except:
133
- return number_str
 
 
 
 
 
 
 
134
 
135
  def convert_english(self, text):
136
- """轉換英文單詞為中文"""
 
 
 
137
  # 按長度排序,先處理長詞彙
138
  sorted_words = sorted(self.conversion_map.keys(), key=len, reverse=True)
139
 
 
140
  for english_word in sorted_words:
141
  if len(english_word) > 1: # 跳過單字母,後面單獨處理
142
  chinese_word = self.conversion_map[english_word]
143
  # 使用單詞邊界匹配,不區分大小寫
144
  pattern = r'\b' + re.escape(english_word) + r'\b'
145
- text = re.sub(pattern, chinese_word, text, flags=re.IGNORECASE)
 
 
 
 
 
146
 
 
 
 
 
 
147
  return text
148
 
149
  def convert_single_letters(self, text):
150
- """轉換單個英文字母"""
 
 
151
  def letter_to_chinese(match):
152
  letter = match.group().lower()
153
- return self.conversion_map.get(letter, letter)
 
 
154
 
155
  # 匹配獨立的英文字母
156
- text = re.sub(r'\b[a-zA-Z]\b', letter_to_chinese, text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  return text
158
 
159
  def convert_text(self, text):
160
- """主要轉換函數"""
161
  if not text:
162
  return text
163
 
164
  original_text = text
165
- print(f"🔄 始文本: {original_text}")
166
 
167
- # 1. 轉換英文單詞
 
 
 
 
 
168
  text = self.convert_english(text)
169
 
170
  # 2. 轉換數字
@@ -173,13 +337,35 @@ class TextConverter:
173
  # 3. 轉換剩餘的單個字母
174
  text = self.convert_single_letters(text)
175
 
176
- # 4. 多餘空格
177
- text = re.sub(r'\s+', ' ', text).strip()
178
 
179
  if text != original_text:
180
- print(f"✅ 轉換後文本: {text}")
 
 
181
 
182
  return text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
 
185
  class TaiwaneseVITSTTS:
@@ -188,9 +374,15 @@ class TaiwaneseVITSTTS:
188
  self.model_dir = Path("./models")
189
  self.dict_dir = Path("./dict")
190
  self.text_converter = TextConverter()
 
191
  self.setup_jieba_dict()
192
  self.setup_model()
193
 
 
 
 
 
 
194
  def setup_jieba_dict(self):
195
  """設置 jieba 字典目錄"""
196
  try:
@@ -275,6 +467,10 @@ class TaiwaneseVITSTTS:
275
  test_audio = self.tts.generate(text="測試", sid=0, speed=1.0)
276
  if len(test_audio.samples) > 0:
277
  print("✅ 模型測試通過!")
 
 
 
 
278
 
279
  except Exception as e:
280
  print(f"❌ 模型設置失敗: {e}")
@@ -282,24 +478,45 @@ class TaiwaneseVITSTTS:
282
  print(f"詳細錯誤: {traceback.format_exc()}")
283
  raise
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  def synthesize(self, text, speed=1.0, enable_conversion=True):
286
- """合成語音"""
287
  if not text or not text.strip():
288
  return None, "❌ 請輸入文本"
289
 
290
  original_text = text.strip()
 
291
 
292
  # 文本轉換
293
  if enable_conversion:
294
  text = self.text_converter.convert_text(original_text)
 
 
295
  else:
296
  text = original_text
 
297
 
298
  if len(text) > 500:
299
  text = text[:500]
 
300
 
301
  try:
302
  print(f"🎤 正在合成語音...")
 
 
303
  if enable_conversion and text != original_text:
304
  print(f"📝 使用轉換後文本: {text}")
305
 
@@ -307,6 +524,8 @@ class TaiwaneseVITSTTS:
307
  samples = audio.samples
308
  sample_rate = audio.sample_rate
309
 
 
 
310
  if len(samples) == 0:
311
  return None, "❌ 語音生成失敗:生成的音頻為空"
312
 
@@ -323,19 +542,52 @@ class TaiwaneseVITSTTS:
323
 
324
  status_info = f"✅ 語音合成成功!\n📊 採樣率: {sample_rate}Hz\n⏱️ 時長: {duration:.2f}秒"
325
  if enable_conversion and text != original_text:
326
- status_info += f"\n🔄 轉換: {original_text} → {text}"
 
 
 
 
327
 
328
  return (sample_rate, audio_array), status_info
329
 
330
  except Exception as e:
331
  error_msg = f"❌ 語音合成失敗: {str(e)}"
332
  print(error_msg)
 
333
  return None, error_msg
334
 
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  # 全局 TTS 實例
337
  print("🔧 正在初始化 TTS 模型...")
338
  try:
 
 
 
339
  tts_model = TaiwaneseVITSTTS()
340
  print("✅ TTS 系統就緒!")
341
  model_status = "🟢 模型已載入"
@@ -354,24 +606,26 @@ def generate_speech(text, speed, enable_conversion):
354
 
355
 
356
  def create_interface():
357
- # 預設範例文本
358
  examples = [
359
  ["你好,歡迎使用繁體中文語音合成系統!", 1.0, True],
360
- ["今天是2024年1月1日,天氣很好。", 1.0, True],
361
- ["我的email是test@gmail.com,請聯繫我。", 1.0, True],
362
- ["這是一個AI技術的demo,使用Python開發。", 1.1, True],
363
  ["Hello world! 這是一個測試。", 1.0, True],
364
- ["iPhone 15Samsung Galaxy哪個比較好?", 0.9, True],
 
 
 
 
 
365
  ]
366
 
367
  device_info = "🎮 GPU" if torch.cuda.is_available() else "💻 CPU"
368
 
369
  with gr.Blocks(
370
- title="繁體中文語音合成 - Breeze2-VITS Enhanced",
371
  theme=gr.themes.Soft(),
372
  css="""
373
  .gradio-container {
374
- max-width: 1000px !important;
375
  margin: auto !important;
376
  }
377
  .status-box {
@@ -389,16 +643,29 @@ def create_interface():
389
  margin: 10px 0;
390
  text-align: center;
391
  }
 
 
 
 
 
 
 
392
  """
393
  ) as demo:
394
 
395
  gr.HTML(f"""
396
  <div class="status-box">
397
- <h1>🎙️ 繁體中文語音合成 - Breeze2-VITS Enhanced</h1>
398
  <p><strong>狀態:</strong> {model_status} | <strong>設備:</strong> {device_info}</p>
399
  </div>
400
  """)
401
 
 
 
 
 
 
 
402
  gr.HTML("""
403
  <div class="feature-box">
404
  <strong>🇹🇼 專業台灣國語 TTS</strong> | 🔄 自動英數轉換 | 🎯 智慧文本處理
@@ -418,7 +685,7 @@ def create_interface():
418
  placeholder="請輸入要合成的文本,支援中文、英文、數字混合...",
419
  lines=5,
420
  max_lines=8,
421
- value="你好!今天是2024年,歡迎使用AI語音合成系統。"
422
  )
423
 
424
  with gr.Row():
@@ -453,9 +720,9 @@ def create_interface():
453
  )
454
 
455
  status_msg = gr.Textbox(
456
- label="📊 狀態資訊",
457
  interactive=False,
458
- lines=5,
459
  value="準備就緒,請輸入文本並點擊生成語音" if tts_model else f"模型載入���敗: {model_status}"
460
  )
461
 
@@ -466,18 +733,61 @@ def create_interface():
466
  outputs=[audio_output, status_msg],
467
  fn=generate_speech,
468
  cache_examples=False,
469
- label="📚 範例文本 (支援中數混合)"
470
  )
471
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  with gr.Accordion("📋 使用說明與功能特色", open=False):
473
  gr.Markdown(f"""
474
  ### 🚀 主要功能
475
 
476
- #### 🔄 智慧文本轉換
477
- - **英文轉換**: hello → 哈囉, AI人工智慧
 
 
478
  - **數字轉換**: 123 → 一二三, 2024 → 二零二四
479
- - **品牌名稱**: Apple蘋果, Google谷歌
480
- - **技術**: API程式介面, CPU中央處理器
481
 
482
  #### 🎯 支援內容
483
  - 繁體中文文本
@@ -485,32 +795,20 @@ def create_interface():
485
  - 阿拉伯數字
486
  - 混合語言文本
487
  - 常見縮寫和品牌
 
488
 
489
  ### 📝 使用技巧
490
- 1. **啟用轉換**: 勾選「啟用英數轉換」自動處理英和數字
491
- 2. **關閉轉換**: 取消勾選以使用原始文本(純中文效果最佳)
492
- 3. **混合文本**: 支援今天天氣很好,temperature25度這樣的混合文本
493
- 4. **專有名詞**: 系統已內建常見品牌和技術詞彙中文發音
494
 
495
  ### 🔧 技術資訊
496
  - **模型**: MediaTek Breeze2-VITS-onnx
497
  - **轉換規則**: {len(tts_model.text_converter.conversion_map) if tts_model else 0} 個內建對照
498
- - **支援格式**: 中文、英文、數字、符號
499
  - **運行設備**: {device_info}
500
  - **模型狀態**: {model_status}
501
-
502
- ### ⚙️ 自定義轉換
503
- 您可以編輯 `text_mapping.txt` 文件來添加自定義的轉換規則:
504
- ```
505
- your_word|您的中文發音
506
- brand_name|品牌中文名
507
- ```
508
-
509
- ### 🛠️ 故障排除
510
- - **英文不發音**: 確保啟用「英數轉換」功能
511
- - **數字不發音**: 檢查轉換功能是否開啟
512
- - **發音不準**: 嘗試關閉轉換使用���中文文本
513
- - **載入失敗**: 檢查模型文件是否完整
514
  """)
515
 
516
  # 事件綁定
 
1
  """
2
+ Breeze2-VITS 繁體中文語音合成 - 英文朗讀問題修復
3
+ 增強調試功能和轉換邏輯
4
  """
5
 
6
  import gradio as gr
 
24
 
25
 
26
  class TextConverter:
27
+ """文本轉換器,將英文和數字轉換為中文發音 - 增強調試版"""
28
 
29
  def __init__(self, mapping_file="text_mapping.txt"):
30
  self.mapping_file = Path(mapping_file)
31
  self.conversion_map = {}
32
+ self.debug_mode = True # 啟用調試模式
33
  self.load_mapping()
34
 
35
  def load_mapping(self):
 
50
  self.conversion_map[original.strip().lower()] = chinese.strip()
51
 
52
  print(f"✅ 載入 {len(self.conversion_map)} 個轉換規則")
53
+
54
+ # 調試:顯示部分轉換規則
55
+ if self.debug_mode:
56
+ print("🔍 部分轉換規則:")
57
+ for i, (k, v) in enumerate(list(self.conversion_map.items())[:10]):
58
+ print(f" {k} → {v}")
59
+ if len(self.conversion_map) > 10:
60
+ print(f" ... 還有 {len(self.conversion_map) - 10} 個規則")
61
+
62
  else:
63
  print(f"⚠️ 轉換對照表文件不存在: {self.mapping_file}")
64
+ self.create_enhanced_mapping()
65
  except Exception as e:
66
  print(f"❌ 載入轉換對照表失敗: {e}")
67
+ self.create_enhanced_mapping()
68
 
69
+ def create_enhanced_mapping(self):
70
+ """創建增強的轉換對照表"""
71
  default_mappings = {
72
  # 數字
73
  '0': '零', '1': '一', '2': '二', '3': '三', '4': '四',
74
  '5': '五', '6': '六', '7': '七', '8': '八', '9': '九',
75
+ '10': '十', '11': '一', '12': '十二', '13': '十三', '14': '十四', '15': '十五',
76
+ '16': '十六', '17': '十七', '18': '十八', '19': '十九', '20': '二十',
77
+ '100': '一百', '1000': '一千', '10000': '一萬',
78
+
79
+ # 基本英文問候語
80
+ 'hello': '哈囉', 'hi': '嗨', 'hey': '嘿', 'bye': '拜拜', 'goodbye': '再見',
81
+ 'yes': '是的', 'no': '不', 'ok': '好的', 'okay': '好的',
82
+ 'good': '好的', 'bad': '不好', 'nice': '很棒', 'great': '很好',
83
+ 'thank': '謝謝', 'thanks': '謝謝', 'please': '請',
84
+ 'sorry': '對不起', 'excuse': '不好意思',
85
+
86
+ # 時間相關
87
+ 'today': '今天', 'tomorrow': '明天', 'yesterday': '昨天',
88
+ 'morning': '早上', 'afternoon': '下午', 'evening': '晚上', 'night': '晚上',
89
+ 'monday': '星期一', 'tuesday': '星期二', 'wednesday': '星期三',
90
+ 'thursday': '星期四', 'friday': '星期五', 'saturday': '星期六', 'sunday': '星期日',
91
 
92
+ # 常用動詞
93
+ 'go': '', 'come': '', 'see': '', 'look': '', 'do': '做', 'make': '做',
94
+ 'get': '得到', 'take': '', 'give': '', 'have': '', 'be': '是',
95
+ 'know': '知道', 'think': '想', 'want': '想要', 'need': '需要',
96
+ 'like': '喜歡', 'love': '愛', 'help': '幫助', 'work': '工作',
97
 
98
  # 技術詞彙
99
+ 'ai': '人工智慧', 'api': '程式介面', 'app': '應用程式', 'web': '網路',
100
+ 'cpu': '中央處理器', 'gpu': '圖形處理器', 'ram': '記憶體',
101
+ 'computer': '電腦', 'laptop': '筆記型電腦', 'phone': '手機', 'mobile': '手機',
102
+ 'internet': '網際網路', 'wifi': '無線網路', 'bluetooth': '藍牙',
103
+ 'software': '軟體', 'hardware': '硬體', 'program': '程式', 'code': '程式碼',
104
+ 'data': '資料', 'database': '資料庫', 'file': '檔案', 'folder': '資料夾',
105
+
106
+ # 品牌名稱
107
+ 'apple': '蘋果', 'google': '谷歌', 'microsoft': '微軟', 'amazon': '亞馬遜',
108
+ 'facebook': '臉書', 'twitter': '推特', 'youtube': '油管', 'instagram': 'instagram',
109
+ 'samsung': '三星', 'sony': '索尼', 'lg': 'LG', 'htc': 'HTC',
110
+ 'iphone': '愛瘋', 'android': '安卓', 'windows': '視窗系統', 'ios': 'iOS',
111
+
112
+ # 常用形容詞
113
+ 'big': '大', 'small': '小', 'new': '新', 'old': '舊',
114
+ 'hot': '熱', 'cold': '冷', 'fast': '快', 'slow': '慢',
115
+ 'easy': '容易', 'hard': '困難', 'simple': '簡單', 'complex': '複雜',
116
+ 'important': '重要', 'useful': '有用', 'interesting': '有趣',
117
+
118
+ # 字母 (更自然的中文發音)
119
+ 'a': '欸', 'b': '比', 'c': '西', 'd': '迪', 'e': '伊',
120
+ 'f': '艾夫', 'g': '吉', 'h': '艾奇', 'i': '愛', 'j': '傑',
121
+ 'k': '凱', 'l': '艾爾', 'm': '艾姆', 'n': '艾恩', 'o': '歐',
122
+ 'p': '皮', 'q': '丘', 'r': '艾爾', 's': '艾斯', 't': '替',
123
+ 'u': '優', 'v': '威', 'w': '達布爾優', 'x': '艾克斯', 'y': '歪', 'z': '萊德',
124
 
125
+ # 縮寫詞
126
+ 'ceo': '執行長', 'cto': '技術長', 'cfo': '財務長',
127
+ 'usa': '美國', 'uk': '英國', 'eu': '歐盟',
128
+ 'nasa': '美國太空總署', 'fbi': '聯邦調查局',
129
+ 'covid': '新冠肺炎', 'dna': 'DNA', 'gps': '全球定位系統',
130
+
131
+ # 網路用語
132
+ 'email': '電子郵件', 'www': '全球資訊網', 'http': 'HTTP',
133
+ 'url': '網址', 'link': '連結', 'click': '點擊',
134
+ 'download': '下載', 'upload': '上傳', 'login': '登入', 'logout': '登出',
135
+
136
+ # 常見英文片語的關鍵詞
137
+ 'how': '如何', 'what': '什麼', 'where': '哪裡', 'when': '什麼時候',
138
+ 'why': '為什麼', 'who': '誰', 'which': '哪個',
139
+ 'this': '這個', 'that': '那個', 'here': '這裡', 'there': '那裡',
140
+ 'and': '和', 'or': '或', 'but': '但是', 'so': '所以',
141
+ 'very': '非常', 'much': '很多', 'many': '很多', 'some': '一些',
142
+ 'all': '全部', 'every': '每個', 'any': '任何',
143
  }
144
 
145
  self.conversion_map = default_mappings
146
+ print(f"✅ 使用增強轉換規則: {len(default_mappings)} 個")
147
+
148
+ def debug_print(self, message):
149
+ """調試打印函數"""
150
+ if self.debug_mode:
151
+ print(f"🔍 [DEBUG] {message}")
152
 
153
  def convert_numbers(self, text):
154
+ """轉換連續數字為中文 - 增強版"""
155
+ self.debug_print(f"數字轉換前: {repr(text)}")
156
+
157
  def number_to_chinese(match):
158
  number = match.group()
159
+ self.debug_print(f"處理數字: {number}")
160
+
161
+ if len(number) <= 2:
162
  result = ""
163
  for digit in number:
164
+ chinese_digit = self.conversion_map.get(digit, digit)
165
+ result += chinese_digit
166
+ self.debug_print(f" {digit} → {chinese_digit}")
167
  return result
168
  else:
169
  # 複雜數字處理
170
+ converted = self.convert_large_number(number)
171
+ self.debug_print(f" 大數字 {number} → {converted}")
172
+ return converted
173
 
174
  # 匹配連續數字
175
+ result = re.sub(r'\d+', number_to_chinese, text)
176
+ if result != text:
177
+ self.debug_print(f"數字轉換後: {repr(result)}")
178
+ return result
179
 
180
  def convert_large_number(self, number_str):
181
+ """轉換大數字為中文 - 改進版"""
182
  try:
183
  num = int(number_str)
184
  if num == 0:
185
  return '零'
186
 
187
+ # 使用更完整的數字轉換
188
+ if str(num) in self.conversion_map:
189
+ return self.conversion_map[str(num)]
190
+
191
  # 簡化的數字轉換(支援到萬)
 
192
  digits = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
193
 
194
  if num < 10:
195
  return digits[num]
196
+ elif num < 20:
197
+ if num == 10:
198
+ return '十'
199
+ else:
200
+ return '十' + digits[num % 10]
201
  elif num < 100:
202
  tens = num // 10
203
  ones = num % 10
 
212
  if remainder > 0:
213
  if remainder < 10:
214
  result += '零' + digits[remainder]
215
+ elif remainder < 20:
216
+ result += '一十' if remainder == 10 else '一十' + digits[remainder % 10]
217
+ else:
218
+ result += self.convert_large_number(str(remainder))
219
+ return result
220
+ elif num < 10000:
221
+ thousands = num // 1000
222
+ remainder = num % 1000
223
+ result = digits[thousands] + '千'
224
+ if remainder > 0:
225
+ if remainder < 100:
226
+ result += '零' + self.convert_large_number(str(remainder))
227
  else:
228
  result += self.convert_large_number(str(remainder))
229
  return result
230
  else:
231
+ # 對於更大的數字,逐位轉換
232
+ result = ""
233
+ for digit in number_str:
234
+ result += digits[int(digit)]
235
+ return result
236
  except:
237
+ # 如果轉換失敗,逐位轉換數字
238
+ result = ""
239
+ for digit in number_str:
240
+ if digit.isdigit():
241
+ result += self.conversion_map.get(digit, digit)
242
+ else:
243
+ result += digit
244
+ return result
245
 
246
  def convert_english(self, text):
247
+ """轉換英文單詞為中文 - 增強調試版"""
248
+ self.debug_print(f"英文轉換前: {repr(text)}")
249
+ original_text = text
250
+
251
  # 按長度排序,先處理長詞彙
252
  sorted_words = sorted(self.conversion_map.keys(), key=len, reverse=True)
253
 
254
+ conversion_count = 0
255
  for english_word in sorted_words:
256
  if len(english_word) > 1: # 跳過單字母,後面單獨處理
257
  chinese_word = self.conversion_map[english_word]
258
  # 使用單詞邊界匹配,不區分大小寫
259
  pattern = r'\b' + re.escape(english_word) + r'\b'
260
+ new_text = re.sub(pattern, chinese_word, text, flags=re.IGNORECASE)
261
+
262
+ if new_text != text:
263
+ self.debug_print(f" 轉換: {english_word} → {chinese_word}")
264
+ conversion_count += 1
265
+ text = new_text
266
 
267
+ if conversion_count > 0:
268
+ self.debug_print(f"英文轉換後: {repr(text)} (共轉換 {conversion_count} 個詞)")
269
+ else:
270
+ self.debug_print("沒有找到可轉換的英文詞彙")
271
+
272
  return text
273
 
274
  def convert_single_letters(self, text):
275
+ """轉換單個英文字母 - 增強版"""
276
+ self.debug_print(f"字母轉換前: {repr(text)}")
277
+
278
  def letter_to_chinese(match):
279
  letter = match.group().lower()
280
+ chinese = self.conversion_map.get(letter, letter)
281
+ self.debug_print(f" 字母轉換: {letter} → {chinese}")
282
+ return chinese
283
 
284
  # 匹配獨立的英文字母
285
+ result = re.sub(r'\b[a-zA-Z]\b', letter_to_chinese, text)
286
+ if result != text:
287
+ self.debug_print(f"字母轉換後: {repr(result)}")
288
+ return result
289
+
290
+ def preprocess_text(self, text):
291
+ """預處理文本 - 處理特殊情況"""
292
+ # 處理常見的英文縮寫
293
+ text = re.sub(r'\bDr\.', 'Doctor', text, flags=re.IGNORECASE)
294
+ text = re.sub(r'\bMr\.', 'Mister', text, flags=re.IGNORECASE)
295
+ text = re.sub(r'\bMrs\.', 'Missis', text, flags=re.IGNORECASE)
296
+ text = re.sub(r'\bMs\.', 'Miss', text, flags=re.IGNORECASE)
297
+
298
+ # 處理email地址中的@符號
299
+ text = re.sub(r'@', ' at ', text)
300
+
301
+ # 處理網址中的點
302
+ text = re.sub(r'\.com\b', ' dot com', text, flags=re.IGNORECASE)
303
+ text = re.sub(r'\.org\b', ' dot org', text, flags=re.IGNORECASE)
304
+ text = re.sub(r'\.net\b', ' dot net', text, flags=re.IGNORECASE)
305
+
306
+ return text
307
+
308
+ def postprocess_text(self, text):
309
+ """後處理文本 - 清理和優化"""
310
+ # 清理多餘空格
311
+ text = re.sub(r'\s+', ' ', text).strip()
312
+
313
+ # 處理標點符號前的空格
314
+ text = re.sub(r'\s+([,。!?;:])', r'\1', text)
315
+
316
  return text
317
 
318
  def convert_text(self, text):
319
+ """主要轉換函數 - 增強調試版"""
320
  if not text:
321
  return text
322
 
323
  original_text = text
324
+ print(f"🔄 轉換文本: {repr(original_text)}")
325
 
326
+ # 預處理
327
+ text = self.preprocess_text(text)
328
+ if text != original_text:
329
+ self.debug_print(f"預處理後: {repr(text)}")
330
+
331
+ # 1. 轉換英文單詞(先處理多字母詞彙)
332
  text = self.convert_english(text)
333
 
334
  # 2. 轉換數字
 
337
  # 3. 轉換剩餘的單個字母
338
  text = self.convert_single_letters(text)
339
 
340
+ # 4. 後處
341
+ text = self.postprocess_text(text)
342
 
343
  if text != original_text:
344
+ print(f"✅ 轉換完成: {repr(original_text)} → {repr(text)}")
345
+ else:
346
+ print(f"ℹ️ 文本未發生變化: {repr(text)}")
347
 
348
  return text
349
+
350
+ def test_conversion(self, test_texts=None):
351
+ """測試轉換功能"""
352
+ if test_texts is None:
353
+ test_texts = [
354
+ "Hello world",
355
+ "I have 123 apples",
356
+ "My email is test@gmail.com",
357
+ "Apple iPhone 15 is good",
358
+ "AI and ML are useful",
359
+ "CPU speed is 3.5 GHz"
360
+ ]
361
+
362
+ print("\n🧪 測試文本轉換功能:")
363
+ print("=" * 50)
364
+ for text in test_texts:
365
+ converted = self.convert_text(text)
366
+ print(f"原文: {text}")
367
+ print(f"轉換: {converted}")
368
+ print("-" * 50)
369
 
370
 
371
  class TaiwaneseVITSTTS:
 
374
  self.model_dir = Path("./models")
375
  self.dict_dir = Path("./dict")
376
  self.text_converter = TextConverter()
377
+ self.debug_mode = True # 啟用調試模式
378
  self.setup_jieba_dict()
379
  self.setup_model()
380
 
381
+ def debug_print(self, message):
382
+ """調試打印函數"""
383
+ if self.debug_mode:
384
+ print(f"🔍 [TTS DEBUG] {message}")
385
+
386
  def setup_jieba_dict(self):
387
  """設置 jieba 字典目錄"""
388
  try:
 
467
  test_audio = self.tts.generate(text="測試", sid=0, speed=1.0)
468
  if len(test_audio.samples) > 0:
469
  print("✅ 模型測試通過!")
470
+
471
+ # 測試轉換功能
472
+ print("\n🧪 測試文本轉換:")
473
+ self.text_converter.test_conversion()
474
 
475
  except Exception as e:
476
  print(f"❌ 模型設置失敗: {e}")
 
478
  print(f"詳細錯誤: {traceback.format_exc()}")
479
  raise
480
 
481
+ def validate_converted_text(self, text):
482
+ """驗證轉換後的文本是否適合TTS"""
483
+ # 檢查是否還有英文字母
484
+ english_chars = re.findall(r'[a-zA-Z]+', text)
485
+ if english_chars:
486
+ self.debug_print(f"警告:轉換後仍有英文字母: {english_chars}")
487
+
488
+ # 檢查是否有不支持的字符
489
+ unsupported_chars = re.findall(r'[^\u4e00-\u9fff\u3000-\u303f\uff00-\uffef\s\d,。!?;:]', text)
490
+ if unsupported_chars:
491
+ self.debug_print(f"警告:發現可能不支持的字符: {set(unsupported_chars)}")
492
+
493
+ return text
494
+
495
  def synthesize(self, text, speed=1.0, enable_conversion=True):
496
+ """合成語音 - 增強調試版"""
497
  if not text or not text.strip():
498
  return None, "❌ 請輸入文本"
499
 
500
  original_text = text.strip()
501
+ self.debug_print(f"開始語音合成,原始文本: {repr(original_text)}")
502
 
503
  # 文本轉換
504
  if enable_conversion:
505
  text = self.text_converter.convert_text(original_text)
506
+ # 驗證轉換結果
507
+ text = self.validate_converted_text(text)
508
  else:
509
  text = original_text
510
+ self.debug_print("跳過文本轉換")
511
 
512
  if len(text) > 500:
513
  text = text[:500]
514
+ self.debug_print("文本過長,已截斷至500字符")
515
 
516
  try:
517
  print(f"🎤 正在合成語音...")
518
+ self.debug_print(f"最終TTS輸入文本: {repr(text)}")
519
+
520
  if enable_conversion and text != original_text:
521
  print(f"📝 使用轉換後文本: {text}")
522
 
 
524
  samples = audio.samples
525
  sample_rate = audio.sample_rate
526
 
527
+ self.debug_print(f"TTS輸出 - 樣本數: {len(samples)}, 採樣率: {sample_rate}")
528
+
529
  if len(samples) == 0:
530
  return None, "❌ 語音生成失敗:生成的音頻為空"
531
 
 
542
 
543
  status_info = f"✅ 語音合成成功!\n📊 採樣率: {sample_rate}Hz\n⏱️ 時長: {duration:.2f}秒"
544
  if enable_conversion and text != original_text:
545
+ status_info += f"\n🔄 文本轉換: {original_text} → {text}"
546
+
547
+ # 添加調試信息
548
+ if self.debug_mode:
549
+ status_info += f"\n🔍 調試信息:\n 原始長度: {len(original_text)}\n 轉換後長度: {len(text)}"
550
 
551
  return (sample_rate, audio_array), status_info
552
 
553
  except Exception as e:
554
  error_msg = f"❌ 語音合成失敗: {str(e)}"
555
  print(error_msg)
556
+ self.debug_print(f"合成失敗詳情: {e}")
557
  return None, error_msg
558
 
559
 
560
+ # 初始化時運行測試
561
+ def run_initialization_tests():
562
+ """運行初始化測試"""
563
+ print("\n" + "="*60)
564
+ print("🔧 運行系統診斷測試")
565
+ print("="*60)
566
+
567
+ # 測試文本轉換器
568
+ converter = TextConverter()
569
+ test_cases = [
570
+ "Hello world",
571
+ "I love Apple iPhone 15",
572
+ "AI technology is amazing",
573
+ "My email is user@gmail.com",
574
+ "CPU speed is 2.5 GHz"
575
+ ]
576
+
577
+ print("\n📝 測試文本轉換功能:")
578
+ for test_text in test_cases:
579
+ result = converter.convert_text(test_text)
580
+ print(f" 輸入: {test_text}")
581
+ print(f" 輸出: {result}")
582
+ print()
583
+
584
+
585
  # 全局 TTS 實例
586
  print("🔧 正在初始化 TTS 模型...")
587
  try:
588
+ # 運行診斷測試
589
+ run_initialization_tests()
590
+
591
  tts_model = TaiwaneseVITSTTS()
592
  print("✅ TTS 系統就緒!")
593
  model_status = "🟢 模型已載入"
 
606
 
607
 
608
  def create_interface():
609
+ # 預設範例文本 - 增加更多測試用例
610
  examples = [
611
  ["你好,歡迎使用繁體中文語音合成系統!", 1.0, True],
 
 
 
612
  ["Hello world! 這是一個測試。", 1.0, True],
613
+ ["I love Apple iPhone 15 and Samsung Galaxy", 1.0, True],
614
+ ["AI technology is amazing, CPU speed is 3.5 GHz", 1.0, True],
615
+ ["My email is test@gmail.com, please contact me", 1.0, True],
616
+ ["今天是2024年1月1日,天氣很好。", 1.0, True],
617
+ ["Google and Microsoft are big tech companies", 1.0, True],
618
+ ["API development with Python is easy", 1.0, True],
619
  ]
620
 
621
  device_info = "🎮 GPU" if torch.cuda.is_available() else "💻 CPU"
622
 
623
  with gr.Blocks(
624
+ title="繁體中文語音合成 - Breeze2-VITS Enhanced Debug",
625
  theme=gr.themes.Soft(),
626
  css="""
627
  .gradio-container {
628
+ max-width: 1200px !important;
629
  margin: auto !important;
630
  }
631
  .status-box {
 
643
  margin: 10px 0;
644
  text-align: center;
645
  }
646
+ .debug-box {
647
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
648
+ color: white;
649
+ padding: 10px 15px;
650
+ border-radius: 8px;
651
+ margin: 10px 0;
652
+ }
653
  """
654
  ) as demo:
655
 
656
  gr.HTML(f"""
657
  <div class="status-box">
658
+ <h1>🎙️ 繁體中文語音合成 - Breeze2-VITS Enhanced Debug</h1>
659
  <p><strong>狀態:</strong> {model_status} | <strong>設備:</strong> {device_info}</p>
660
  </div>
661
  """)
662
 
663
+ gr.HTML("""
664
+ <div class="debug-box">
665
+ <strong>🔍 調試增強版</strong> | 詳細轉換日志 | 問題診斷 | 性能分析
666
+ </div>
667
+ """)
668
+
669
  gr.HTML("""
670
  <div class="feature-box">
671
  <strong>🇹🇼 專業台灣國語 TTS</strong> | 🔄 自動英數轉換 | 🎯 智慧文本處理
 
685
  placeholder="請輸入要合成的文本,支援中文、英文、數字混合...",
686
  lines=5,
687
  max_lines=8,
688
+ value="Hello world! 今天是2024年,歡迎使用AI語音合成系統。"
689
  )
690
 
691
  with gr.Row():
 
720
  )
721
 
722
  status_msg = gr.Textbox(
723
+ label="📊 狀態資訊與調試信息",
724
  interactive=False,
725
+ lines=8,
726
  value="準備就緒,請輸入文本並點擊生成語音" if tts_model else f"模型載入���敗: {model_status}"
727
  )
728
 
 
733
  outputs=[audio_output, status_msg],
734
  fn=generate_speech,
735
  cache_examples=False,
736
+ label="📚 範例文本 (包含文朗讀測試)"
737
  )
738
 
739
+ with gr.Accordion("🔍 調試信息與故障排除", open=True):
740
+ gr.Markdown(f"""
741
+ ### 🚀 調試功能
742
+
743
+ #### 🔄 轉換規則狀態
744
+ - **載入規則數**: {len(tts_model.text_converter.conversion_map) if tts_model else 0} 個
745
+ - **調試模式**: {'✅ 已啟用' if tts_model and tts_model.debug_mode else '❌ 未啟用'}
746
+ - **模型狀態**: {model_status}
747
+
748
+ #### 🧪 常見問題診斷
749
+
750
+ **問題1: 英文不發音**
751
+ - ✅ 確保啟用「英數轉換」功能
752
+ - ✅ 檢查控制台轉換日志
753
+ - ✅ 測試單獨的英文單詞
754
+
755
+ **問題2: 轉換後仍有英文**
756
+ - 可能是詞典中缺少該詞彙
757
+ - 查看調試信息中的轉換過程
758
+ - 考慮添加自定義轉換規則
759
+
760
+ **問題3: 發音不自然**
761
+ - 嘗試調整轉換後的中文用詞
762
+ - 使用更常見的中文表達
763
+ - 關閉轉換使用純中文測試
764
+
765
+ #### 🔧 調試步驟
766
+ 1. 打開瀏覽器開發者工具查看控制台
767
+ 2. 輸入測試文本並生成語音
768
+ 3. 觀察轉換過程的調試信息
769
+ 4. 檢查哪些詞彙被成功轉換
770
+ 5. 分析未轉換詞彙的原因
771
+
772
+ #### 📝 測試建議
773
+ - 先測試純英文: "Hello world"
774
+ - 再測試中英混合: "Hello 世界"
775
+ - 測試數字: "I have 123 apples"
776
+ - 測試品牌: "Apple iPhone Samsung"
777
+ - 測試技術詞彙: "AI CPU GPU API"
778
+ """)
779
+
780
  with gr.Accordion("📋 使用說明與功能特色", open=False):
781
  gr.Markdown(f"""
782
  ### 🚀 主要功能
783
 
784
+ #### 🔄 智慧文本轉換 (增強版)
785
+ - **基本英文**: hello → 哈囉, good好的, thank → 謝謝
786
+ - **技術詞彙**: AI → 人工智慧, CPU → 中央處理器, API → 程式介面
787
+ - **品牌名稱**: Apple → 蘋果, Google → 谷歌, iPhone → 愛瘋
788
  - **數字轉換**: 123 → 一二三, 2024 → 二零二四
789
+ - **字母發音**: A, B比, C → 西
790
+ - **縮寫詞**: CEO執行長, USA美國, GPS → 全球定位系統
791
 
792
  #### 🎯 支援內容
793
  - 繁體中文文本
 
795
  - 阿拉伯數字
796
  - 混合語言文本
797
  - 常見縮寫和品牌
798
+ - 網路用語和技術術語
799
 
800
  ### 📝 使用技巧
801
+ 1. **測試英文**: 使範例中的英文測試案例
802
+ 2. **調試轉換**: 查看控制台的詳細轉換過程
803
+ 3. **混合文本**: 嘗試Hello world 這測試
804
+ 4. **數字處理**: 測試不同長度數字
805
 
806
  ### 🔧 技術資訊
807
  - **模型**: MediaTek Breeze2-VITS-onnx
808
  - **轉換規則**: {len(tts_model.text_converter.conversion_map) if tts_model else 0} 個內建對照
809
+ - **調試模式**: {'啟用' if tts_model and tts_model.debug_mode else '未啟用'}
810
  - **運行設備**: {device_info}
811
  - **模型狀態**: {model_status}
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  """)
813
 
814
  # 事件綁定