mailer.txt的內容:
《貓頭鷹郵差與最後一封信》
在一座寧靜的森林裡,住著一隻年紀很大的貓頭鷹,大家都叫牠「歐里伯爺爺」。他不是一般的貓頭鷹,而是一位非常敬業的森林郵差。每天清晨,他都會準時展開翅膀,把信送到狐狸奶奶、小熊一家、刺蝟老師那裡。即使下雨或起霧,他也從不缺席。
森林裡的動物們都非常喜歡他送信時溫暖的笑容與溫柔的問候,有時信封裡還會夾一朵小花或一片秋天的紅葉。他的信不只傳遞消息,更傳遞了溫度。
但隨著年紀越來越大,歐里伯的飛行速度也慢了下來。終於有一天,他決定退休,讓年輕的白鴿小雪接手工作。
「我要送出最後一封信,再安心退休。」他拍拍翅膀,笑著對大家說。
那是一封沒有署名的信,信封是粉紅色的,上面只寫了一個名字:「小松鼠可可」。
「這孩子搬家好幾次,我得想辦法找到她,這封信不能遲到。」歐里伯想。
他飛過溪流、穿過老橡樹林、越過大霧山谷,一路打聽著小松鼠可可的下落。途中,他遇見了搬著堅果的小老鼠,摘蘋果的野兔,還有躲雨的山羊,每個人都幫他出了一點力。
終於,在一棵長滿松果的大樹下,他找到了可可。
「歐里伯爺爺!」可可開心地撲到他身上,「我以為你退休了!」
「還沒呢,這封信,是你媽媽留給你的,她半年前搬到另一片森林前託我交給你。」
可可小心地打開信,裡面寫著:
「親愛的可可,
雖然媽媽現在不在你身邊,但我一直都以你為榮。希望你每天都勇敢、快樂,記得幫助別人,因為你擁有一顆溫暖的心。
愛你的媽媽。」
讀著讀著,小松鼠的眼眶紅了。她緊緊抱著信,也抱著歐里伯。
「謝謝你,爺爺。」
那一刻,歐里伯感覺整顆心都亮了起來。他知道自己這一生最重要的事,就是把這封信送到小可可手中。
從那天起,小松鼠可可也開始幫忙做一些送信的小差事。她學著像歐里伯一樣,笑著和大家說早安,把祝福帶給每一位朋友。
森林裡的郵差換人了,但溫暖和善意,依然在森林裡飛翔著。
code:
import os
import json
from openai import OpenAI
from pathlib import Path
import time
text_file="mailer.txt"
def openai_text_to_speech(text_file=text_file, output_file=f"{os.path.splitext(text_file)[0]}.mp3"):
"""
使用 OpenAI TTS API 將文字轉換為語音
優勢:
- 最高品質的語音合成
- 多種語音選擇
- 支援多語言
- 語音自然度極佳
"""
try:
# 讀取 API Key
with open(r"D:\user\Python\GPT\json\api_key.json", "r") as f:
api_key = json.load(f)["api_key"]
# 初始化 OpenAI 客戶端
client = OpenAI(api_key=api_key)
# 讀取文字內容
if not os.path.exists(text_file):
# 建立故事文字檔
story_text = """《貓頭鷹郵差與最後一封信》
在一座寧靜的森林裡,住著一隻年紀很大的貓頭鷹,大家都叫牠「歐里伯爺爺」。他不是一般的貓頭鷹,而是一位非常敬業的森林郵差。每天清晨,他都會準時展開翅膀,把信送到狐狸奶奶、小熊一家、刺蝟老師那裡。即使下雨或起霧,他也從不缺席。
森林裡的動物們都非常喜歡他送信時溫暖的笑容與溫柔的問候,有時信封裡還會夾一朵小花或一片秋天的紅葉。他的信不只傳遞消息,更傳遞了溫度。
但隨著年紀越來越大,歐里伯的飛行速度也慢了下來。終於有一天,他決定退休,讓年輕的白鴿小雪接手工作。
「我要送出最後一封信,再安心退休。」他拍拍翅膀,笑著對大家說。
那是一封沒有署名的信,信封是粉紅色的,上面只寫了一個名字:「小松鼠可可」。
「這孩子搬家好幾次,我得想辦法找到她,這封信不能遲到。」歐里伯想。
他飛過溪流、穿過老橡樹林、越過大霧山谷,一路打聽著小松鼠可可的下落。途中,他遇見了搬著堅果的小老鼠,摘蘋果的野兔,還有躲雨的山羊,每個人都幫他出了一點力。
終於,在一棵長滿松果的大樹下,他找到了可可。
「歐里伯爺爺!」可可開心地撲到他身上,「我以為你退休了!」
「還沒呢,這封信,是你媽媽留給你的,她半年前搬到另一片森林前託我交給你。」
可可小心地打開信,裡面寫著:
「親愛的可可,
雖然媽媽現在不在你身邊,但我一直都以你為榮。希望你每天都勇敢、快樂,記得幫助別人,因為你擁有一顆溫暖的心。
愛你的媽媽。」
讀著讀著,小松鼠的眼眶紅了。她緊緊抱著信,也抱著歐里伯。
「謝謝你,爺爺。」
那一刻,歐里伯感覺整顆心都亮了起來。他知道自己這一生最重要的事,就是把這封信送到小可可手中。
從那天起,小松鼠可可也開始幫忙做一些送信的小差事。她學著像歐里伯一樣,笑著和大家說早安,把祝福帶給每一位朋友。
森林裡的郵差換人了,但溫暖和善意,依然在森林裡飛翔著。"""
with open(text_file, "w", encoding="utf-8") as f:
f.write(story_text)
print(f"
已建立文字檔: {text_file}")
with open(text_file, "r", encoding="utf-8") as f:
text_content = f.read()
print(f"
讀取檔案: {text_file}")
print(f"
文字長度: {len(text_content)} 字元")
# OpenAI TTS 語音選項
voices = {
"1": "alloy", # 中性、平衡
"2": "echo", # 男性、深沉
"3": "fable", # 英式、優雅
"4": "onyx", # 男性、深沉
"5": "nova", # 女性、年輕
"6": "shimmer" # 女性、溫暖
}
print("
可用語音:")
for key, voice in voices.items():
print(f" {key}. {voice}")
voice_choice = input("選擇語音 (1-6, 預設為 alloy): ") or "1"
selected_voice = voices.get(voice_choice, "alloy")
print(f"
使用語音: {selected_voice}")
print("
正在生成語音...")
start_time = time.time()
# 呼叫 OpenAI TTS API
response = client.audio.speech.create(
model="tts-1-hd", # 使用高品質模型 (tts-1 為標準模型)
voice=selected_voice,
input=text_content,
response_format="mp3" # 可選: mp3, opus, aac, flac
)
# 儲存音檔
response.stream_to_file(output_file)
end_time = time.time()
process_time = end_time - start_time
print(f"
語音檔案已生成: {output_file}")
print(f"
處理時間: {process_time:.2f} 秒")
# 顯示檔案資訊
file_size = os.path.getsize(output_file) / (1024 * 1024)
print(f"
檔案大小: {file_size:.2f} MB")
# 計算成本 (估算)
char_count = len(text_content)
estimated_cost = char_count * 0.000015 # $0.015 per 1K characters
print(f"
預估費用: ${estimated_cost:.4f} USD")
return True
except Exception as e:
print(f"
生成失敗: {e}")
return False
def test_voice_samples():
"""測試不同語音樣本"""
test_text = "你好,這是語音測試。歡迎來到我們的 AI 工作坊。"
with open(r"D:\user\Python\GPT\json\api_key.json", "r") as f:
api_key = json.load(f)["api_key"]
client = OpenAI(api_key=api_key)
voices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
print("
生成語音樣本...")
for voice in voices:
try:
response = client.audio.speech.create(
model="tts-1",
voice=voice,
input=test_text
)
sample_file = f"sample_{voice}.mp3"
response.stream_to_file(sample_file)
print(f"
{voice}: {sample_file}")
except Exception as e:
print(f"
{voice} 失敗: {e}")
if __name__ == "__main__":
print("
OpenAI TTS 語音合成")
print("=" * 50)
# 詢問是否要測試語音樣本
test_samples = input("
是否先生成語音樣本?(y/n): ")
if test_samples.lower() == 'y':
test_voice_samples()
print()
# 生成完整故事語音
success = openai_text_to_speech()
if success:
print("
OpenAI TTS 轉換完成!")
print(f"
現在可以播放 {os.path.splitext(text_file)[0]}.mp3")
code:

程式碼詳解
1. 匯入套件
import os # 檔案系統操作
import json # JSON 檔案處理
from openai import OpenAI # OpenAI 客戶端
from pathlib import Path # 路徑處理
import time # 時間計算
2. 函數定義與參數
text_file="mailer.txt" # 預設輸入檔案
def openai_text_to_speech(
text_file=text_file,
output_file=f"{os.path.splitext(text_file)[0]}.mp3"
):
參數說明:
text_file
: 輸入的文字檔案名稱output_file
: 輸出的 MP3 檔案名稱(自動從文字檔名生成)
3. API Key 讀取
# 讀取 API Key
with open(r"D:\user\Python\GPT\json\api_key.json", "r") as f:
api_key = json.load(f)["api_key"]
# 初始化 OpenAI 客戶端
client = OpenAI(api_key=api_key)
重要提醒:
- 請修改路徑為你的實際 API Key 檔案路徑
- 確保檔案格式為 UTF-8 編碼
4. 文字檔案處理
# 檢查檔案是否存在
if not os.path.exists(text_file):
# 如果不存在,建立預設故事文字檔
story_text = """故事內容..."""
with open(text_file, "w", encoding="utf-8") as f:
f.write(story_text)
print(f"
已建立文字檔: {text_file}")
# 讀取文字內容
with open(text_file, "r", encoding="utf-8") as f:
text_content = f.read()
編碼重點:
- 使用
encoding="utf-8"
確保中文字元正確處理 - 自動建立預設文字檔案,方便測試
5. 語音選擇系統
# OpenAI TTS 語音選項
voices = {
"1": "alloy", # 中性、平衡
"2": "echo", # 男性、深沉
"3": "fable", # 英式、優雅
"4": "onyx", # 男性、深沉
"5": "nova", # 女性、年輕
"6": "shimmer" # 女性、溫暖
}
# 使用者選擇
voice_choice = input("選擇語音 (1-6, 預設為 alloy): ") or "1"
selected_voice = voices.get(voice_choice, "alloy")
voice_choice = input(“選擇語音 (1-6, 預設為 alloy): “) or “1”
運作邏輯
input()
執行 → 使用者輸入或按 Enter(獲得空字串,視為False)- 真值判斷 → Python 檢查輸入值是否為「真」
or
運算 → 如果左邊為「假」,返回右邊的值
語音特色:
- alloy: 平衡中性,適合多數用途
- echo: 男性聲音,適合正式內容
- nova: 女性聲音,適合親切內容
- shimmer: 溫暖女聲,適合故事朗讀
6. OpenAI TTS API 調用
# 計時開始
start_time = time.time()
# 呼叫 OpenAI TTS API
response = client.audio.speech.create(
model="tts-1-hd", # 高品質模型
voice=selected_voice, # 選擇的語音
input=text_content, # 輸入文字
response_format="mp3" # 輸出格式
)
# 儲存音檔
response.stream_to_file(output_file)
# 計時結束
end_time = time.time()
模型選擇:
tts-1
: 標準模型,較快且便宜tts-1-hd
: 高品質模型,音質更佳但較貴
輸出格式選項:
mp3
: 最常用,檔案小opus
: 適合即時應用aac
: Apple 生態系統flac
: 無損音質質
- 7. 結果報告
# 處理時間
process_time = end_time - start_time
print(f"
處理時間: {process_time:.2f} 秒")
# 檔案大小
file_size = os.path.getsize(output_file) / (1024 * 1024)
print(f"
檔案大小: {file_size:.2f} MB")
# 費用估算
char_count = len(text_content)
estimated_cost = char_count * 0.000015 # $0.015 per 1K characters
print(f"
預估費用: ${estimated_cost:.4f} USD")
輸出的mp3檔:

推薦hahow線上學習python: https://igrape.net/30afN
Google Text-to-Speech (gTTS)
簡介
- 一款基於 Google Translate 的開源 Python 庫,支持多語言文字轉語音。
- 非常輕量、易用,適合快速生成語音文件。
特點
- 支持語言:100+ 種語言,包括中文。
- 免費:僅需網路連線即可使用。
- 輸出格式:生成
.mp3
文件。
安裝
pip install gtts
使用範例
from gtts import gTTS
text = "你好,歡迎使用文字轉語音工具!"
tts = gTTS(text, lang='zh')
tts.save("output.mp3")
優勢
- 簡單易用,快速生成語音文件。
- 無需本地計算資源,適合低性能設備。
限制
- 需要穩定的網路連線。
- 語音自然度稍低,適合基礎 TTS 需求。
推薦hahow線上學習python: https://igrape.net/30afN
Edge-TTS(推薦)
- Microsoft Edge 瀏覽器的 TTS
- Python 套件:
edge-tts
- 音質優秀,完全免費
- 支援 SSML 標記
# 安裝:pip install edge-tts
import edge_tts
import asyncio
async def main():
text = "你好,這是測試"
voice = "zh-TW-HsiaoChenNeural"
communicate = edge_tts.Communicate(text, voice)
await communicate.save("output.mp3")
asyncio.run(main())
程式碼詳細解釋:
1. 安裝套件
# 安裝:pip install edge-tts
- 這是在終端機/命令提示字元中執行的指令
- 安裝 Microsoft Edge 瀏覽器的 TTS 引擎 Python 套件
2. 導入必要模組
import edge_tts # Edge TTS 主要功能
import asyncio # Python 的非同步程式設計模組
3. 非同步函數定義
async def main():
async def
定義一個非同步函數- 非同步函數可以使用
await
關鍵字 - 必須在特殊環境(event loop)中執行
4. 設定參數
text = "你好,這是測試" # 要轉換的文字
voice = "zh-TW-HsiaoChenNeural" # 語音角色(台灣女聲)
5. 建立通訊物件
communicate = edge_tts.Communicate(text, voice)
- 創建一個 Communicate 物件
- 負責與 Edge TTS 服務通訊
6. 儲存音檔
await communicate.save("output.mp3")
await
等待非同步操作完成- 將語音儲存為 output.mp3
7. 執行主程式
asyncio.run(main())
- 創建新的 event loop 並執行 main()
- 這裡是問題所在!
為什麼會出錯?
Event Loop 概念圖解:
正常 Python 環境:
┌─────────────────┐
│ Python Script │
│ │
│ asyncio.run() ──┼──> 創建新 Event Loop ✓
│ │
└─────────────────┘
Jupyter/IPython 環境:
┌─────────────────────┐
│ Jupyter Notebook │
│ │
│ 已有 Event Loop 運行 │ <── 這裡已經有了!
│ │
│ asyncio.run() ──────┼──> 試圖創建新 Loop ✗ (錯誤!)
│ │
└─────────────────────┘
什麼是 Event Loop?
想像一個餐廳:
- 同步程式 = 一個服務生,一次只服務一桌
- 非同步程式 = 一個服務生,同時處理多桌
- Event Loop = 服務生的工作流程管理系統
# 同步方式(傳統)
def 點餐():
顧客A點餐() # 等 A 點完
顧客B點餐() # 再等 B 點完
顧客C點餐() # 最後等 C
# 非同步方式(edge-tts 使用)
async def 點餐():
await 顧客A點餐() # A 在想的時候
await 顧客B點餐() # 可以先問 B
await 顧客C點餐() # 也可以處理 C
正確的使用方式:
選項 1:純 Python 腳本(原始寫法正確)
# script.py
import edge_tts
import asyncio
async def main():
text = "你好,這是測試"
voice = "zh-TW-HsiaoChenNeural"
communicate = edge_tts.Communicate(text, voice)
await communicate.save("output.mp3")
print("語音檔案已生成:output.mp3")
# 在終端機執行:python script.py
if __name__ == "__main__":
asyncio.run(main()) # ✓ 這裡正確!
選項 2:Jupyter Notebook 中使用
# 在 Jupyter cell 中
import edge_tts
text = "你好,這是測試"
voice = "zh-TW-HsiaoChenNeural"
communicate = edge_tts.Communicate(text, voice)
await communicate.save("output.mp3") # 直接 await,不用 asyncio.run()
選項 3:通用解決方案
import edge_tts
import asyncio
import nest_asyncio
# 允許巢狀 event loop
nest_asyncio.apply()
async def text_to_speech(text, filename="output.mp3"):
"""
將文字轉換為語音檔案
參數:
text: 要轉換的文字
filename: 輸出檔案名稱
"""
voice = "zh-TW-HsiaoChenNeural"
print(f"正在生成語音:{text[:20]}...")
communicate = edge_tts.Communicate(text, voice)
await communicate.save(filename)
print(f"完成!檔案儲存為:{filename}")
# 這樣在任何環境都能用
asyncio.run(text_to_speech("歡迎使用語音合成系統"))
簡單類比解釋:
同步 vs 非同步
同步(傳統方式):
下載檔案A (等3秒) ━━━━━━━━━━
下載檔案B (等2秒) ━━━━━━
下載檔案C (等1秒) ━━
總時間:6秒
非同步(edge-tts方式):
下載檔案A ━━━━━━━━━━
下載檔案B ━━━━━━
下載檔案C ━━
總時間:3秒(同時進行)
完整實用範例:
# tts_helper.py
import edge_tts
import asyncio
import os
class SimpleTTS:
"""簡單的 TTS 工具類"""
def __init__(self):
# 設定預設語音
self.default_voice = "zh-TW-HsiaoChenNeural"
def create_speech(self, text, output_file="speech.mp3", voice=None):
"""
同步方法:生成語音檔案
"""
async def _async_create():
voice_to_use = voice or self.default_voice
communicate = edge_tts.Communicate(text, voice_to_use)
await communicate.save(output_file)
# 智慧判斷如何執行
try:
# 嘗試取得現有的 event loop
loop = asyncio.get_running_loop()
# 如果成功,表示在 Jupyter 等環境
import nest_asyncio
nest_asyncio.apply()
loop.run_until_complete(_async_create())
except RuntimeError:
# 如果失敗,表示在一般 Python 環境
asyncio.run(_async_create())
print(f"
語音已儲存至:{output_file}")
return output_file
# 使用方式
tts = SimpleTTS()
tts.create_speech("這是一個測試")
tts.create_speech("Hello World", "english.mp3", "en-US-AriaNeural")
重點整理:
- edge-tts 使用非同步設計是為了效能(可同時處理多個請求)
- asyncio.run() 會創建新的 event loop
- Jupyter 已有 event loop,所以不能再創建
- 解決方式:
- 純 Python:直接用原始寫法
- Jupyter:用 await 不用 asyncio.run()
- 通用:用 nest_asyncio
推薦hahow線上學習python: https://igrape.net/30afN
近期留言