Amazon Polly で
問題文を読み上げ
Takanori Suzuki
Python Kansai #03 / 2024 Apr 17
アジェンダ 📜
背景とゴール
Amazon Pollyの基本
読み上げをカスタマイズ
問題文読み上げでやったこと
背景 🏞️
学習教材の 電子化 プロジェクト
合理的配慮 の一環としてのテキスト読み上げ
全盲の人向けではなく、聴覚優位 の人向け
合理的配慮
合理的配慮とは、障害者から何らかの助けを求める意思の表明があった場合、過度な負担になり過ぎない範囲で、社会的障壁を取り除くために必要な便宜のことである。
聴覚優位
子どもたちの情報の取り入れ方を下記の3タイプに分類し、“知覚の優位性”という考え方が世界に広がっていったことに始まります。
視覚優先型・聴覚優先型・運動感覚/触覚優先型
やりたいこと
問題文などを 読み上げ られる
聴覚優位 の生徒が理解しやすく
完全な読み上げじゃなくてもよい
ちなみに全盲の場合
OSのアクセシビリティ機能を使う
PC上のスクリーンリーダーを使用する
Web側はアクセシビリティに対応する
→今回は対象外
ゴール 🥅
Amazon Polly での音声合成を知る
Pythonでの 実装方法 を知る
読み上げの カスタマイズ方法 を知る
Photos 📷 Tweets 🐦 👍
#pythonkansai
/ #mixleap
@takanory
Slides / スライド 💻
Who am I? / お前 誰よ 👤
Takanori Suzuki / 鈴木 たかのり ( @takanory)
BeProud 取締役 / Python Climber
PyCon JP Association 代表理事
Python Boot Camp 講師、Python mini Hack-a-thon 主催、Pythonボルダリング部 部長
PyCon JP Association 🐍
日本国内のPythonユーザのために、Pythonの普及及び開発支援を行うために、継続的にカンファレンス(PyCon)を開くことを目的とした 非営利組織
PyCon JP 2024 🇯🇵
9月27-29日 に 東京 で開催予定
3名の 共同座長(with 吉田さん、寺田さん)
主催メンバー 募集中
BeProud Inc. 🏢
PyPro4 📕
2024年2月16日発売、468ページ、3,300円
Amazon Pollyの基本 🗣️
Amaozon Polly
数十の言語 で高品質で自然な人間の声を展開
12ヶ月間、 毎月 500万文字が無料
クラウド型コールセンターのAmazon Connectでも使える
Amazon Pollyの画面
画面でテキストを入力して読み上げできる
Amazon Pollyの画面
Amazon Pollyの画面からmp3をダウンロード
「私の戦闘力は530000です」(power-ja.mp3)
「My power level is 530,000」(power-en.mp3)
PythonからAmazon Pollyを実行
Boto3をインストール
APIを使うために環境変数を設定
$ python3.12 -m venv env
$ . env/bin/activate
(env) $ pip install boto3
(env) $ export AWS_ACCESS_KEY_ID=AKI...
(env) $ export AWS_SECRET_ACCESS_KEY=ZoWb...
(env) $ export AWS_DEFAULT_REGION=ap-northeast-1
PythonからAmazon Pollyを実行
from contextlib import closing
from pathlib import Path
import boto3
polly = boto3.client('polly') # クライアント作成
s = "きえろぶっとばされんうちにな"
# テキストから音声を合成
result = polly.synthesize_speech(
Text=s, OutputFormat="mp3", VoiceId="Mizuki")
# 音声を読み込んでファイルに保存
with closing(result["AudioStream"]) as stream:
Path("yamcha-kiero.mp3").write_bytes(stream.read())
PythonからAmazon Pollyを実行
mp3ファイルができた!! 🎉
s = "きえろぶっとばされんうちにな"
# テキストから音声を合成
result = polly.synthesize_speech(
Text=s, OutputFormat="mp3", VoiceId="Mizuki")
Amazon Pollyの基本まとめ 🗣️
AWSの画面 から使える
Pythonでは Boto3 経由で使える
読み上げをカスタマイズ 🔧
言語の変更
VoiceId
引数で 言語と音声 を指定参考: Amazon Polly の音声
日本語: Mizuki, Takumi, Kazuha, Tomoko
英語: Ivy, Salli, Joey, Justinなど
result = polly.synthesize_speech(
Text="超サイヤ人孫悟空だ!!!!!",
OutputFormat="mp3", VoiceId="Takumi")
読みの指定
超サイヤ人の「超」を スーパー と読ませたい
<phoneme>
タグでフリガナを指定参考: 発音記号を使用する
全体を
<speak>
タグで囲む
s = ('<speak><phoneme type="ruby" ph="すーぱー">超</phoneme>'
'サイヤ人孫悟空だ!!!!!</speak>')
読みの指定
TextType="ssml"
引数を追加
result = polly.synthesize_speech(
Text=s, TextType="ssml", OutputFormat="mp3", VoiceId="Takumi")
SSMLタグ
音声合成マークアップ言語(SSML) に対応
段落を区切る(
<p>
)強調する(
<emphasis>
)呼吸音(
<amazon:breakth>
)など
参考:
SSMLタグ
s1 = "クリリンのことか……クリリンのことかーーーっ!!!!!"
# 間を開ける、呼吸音追加、強調する
s2 = ('<speak><p>クリリンのことか……</p>'
'<amazon:breath duration="medium" volume="x-loud"/>'
'<p><emphasis level="strong">クリリンのことかーーーっ!!!!!'
'</emphasis></p></speak>')
読み上げをカスタマイズまとめ 🔧
言語 と 音声 を変更できる
読み を指定できる
SSMLタグ でカスタマイズできる
Lexicon で読みをカスタマイズ 🛠️
Lexicon とは
発音レキシコン: 発音の定義ファイル
<phoneme>
は 個別、レキシコンは 共通複数ファイルを用意して使い分けも可能
参考: レキシコンの管理
Lexiconを使用する(画面)
Lexiconファイルを作成
<?xml version="1.0" encoding="UTF-8"?>
<lexicon version="1.0" xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"
alphabet="ipa" xml:lang="ja">
<lexeme>
<grapheme>超サイヤ人</grapheme>
<alias>スーパーさいやじん</alias>
</lexeme>
<lexeme>
<grapheme>魔貫光殺砲</grapheme>
<alias>まかんこうさっぽう</alias>
</lexeme>
<lexeme>
<grapheme>()</grapheme><alias>カッコ</alias>
</lexeme>
<lexeme>
<grapheme>①</grapheme><alias>まるいち</alias>
</lexeme>
<lexeme>
<grapheme>②</grapheme><alias>まるに</alias>
</lexeme>
<lexeme>
<grapheme>③</grapheme><alias>まるさん</alias>
</lexeme>
<lexeme>
<grapheme>④</grapheme><alias>まるよん</alias>
</lexeme>
</lexicon>
Lexiconを使用する(画面)
名前を付けてXMLファイルをアップロード
Lexiconを使用する(画面)
レキシコンを指定する
Lexiconを使用する(画面)
PythonからLexiconを使用
# Lexiconを名前をつけて登録
LEXICON = "DBLexicon"
data = Path("db-lexicon.xml").read_text(encoding="utf-8")
polly.put_lexicon(Name=LEXICON, Content=data)
s = "魔貫光殺砲うけてみろーーーーーーっ!!!!"
# Lexiconを使用しない
result = polly.synthesize_speech(
Text=s, OutputFormat="mp3", VoiceId="Takumi")
# Lexiconを使用
result = polly.synthesize_speech(
Text=s, OutputFormat="mp3", VoiceId="Takumi",
LexiconNames=[LEXICON])
Lexiconで読みをカスタマイズまとめ 🛠️
Lexiconファイル をXMLで作成
Lexiconファイルを 登録
任意のLexiconを 適用
問題文読み上げでやったこと 📖
Lexicon を作成
①、②:まるいち、まるに
()〔〕:括弧
〜:から
→:やじるし
+:プラス
・:、 (句点と同じ空白が入る)
スペースを <break>
タグに
スペース部分を 一時停止タグ に置換
選択肢のラベルを区切って読む
〜〜を選べ ① ほげ ② ふが
↓
〜〜を選べ<break strength="x-strong"/>①<break strength="x-strong"/>ほげ<break strength="x-strong"/>②<break strength="x-strong"/>ふが
フリガナを <phoneme>
タグに
問題文はHTML形式
フリガナはHTMLの
<ruby>
タグ
<ruby>魔貫光殺砲<rt>まかんこうさっぽう</rt></ruby>
↓
<phoneme type="ruby" ph="まかんこうさっぽう">魔貫光殺砲</phoneme>
日本語の英語の混ざった文章
日本語音声で英語を読ませると発音がやばい
例:Hang in there Kakarot, you are number one!
日本語の英語の混ざった文章
英語の問題文は日本語と英語が 混ざっている
日本語と英語に 分割 し音声読み上げ
1つのmp3にまとめる
日本語の英語の混ざった文章
日本語と英語の混ざった文の例
vegeta-english.txt
次の日本語を英訳したときの()に当てはまる単語を選べ。
「がんばれカカロット お前がナンバーワンだ!」
Hang in there Kakarot, you are () one!
① only ② number ③ minus ④ another
日本語の英語の混ざった文章
()、①を読ませるために Lexicon を登録
<lexeme>
<grapheme>()</grapheme><alias>カッコ</alias>
</lexeme>
<lexeme>
<grapheme>①</grapheme><alias>まるいち</alias>
</lexeme>
<lexeme>
<grapheme>②</grapheme><alias>まるに</alias>
</lexeme>
# Lexiconを名前をつけて登録
LEXICON = "DBLexicon"
data = Path("db-lexicon.xml").read_text(encoding="utf-8")
polly.put_lexicon(Name=LEXICON, Content=data)
日本語の英語の混ざった文章
指定した言語で読み上げる 関数
def text2speech(polly, text, f, lang):
"""指定した言語で読み上げる"""
# 言語で音声切り替え
voice = {"en": "Matthew", "ja": "Takumi"}[lang]
result = polly.synthesize_speech(Text=text,
OutputFormat="mp3", VoiceId=voice,
LexiconNames=[LEXICON])
with closing(result["AudioStream"]) as stream:
f.write(stream.read())
日本語の英語の混ざった文章
正規表現で 日英を分割 して読み上げ
# ファイルから問題文読み込み
p = Path("vegeta-english.txt")
text = p.read_text(encoding="utf-8")
with open("vegeta-question.mp3", "wb") as f:
# 日本語と英語に分割
while m := re.search(r"(^|[\s\b])(\w[\w\s/\.,:;'\"!?]*)([\s\b]|$)", text, re.A):
en_text = m.group(2)
ja_text, text = text.split(en_text, maxsplit=1)
text2speech(polly, ja_text, f, "ja")
text2speech(polly, en_text, f, "en")
if text.strip():
text2speech(polly, ja_text, f, "ja")
日本語の英語の混ざった文章
日本語と英語の混ざった音声ができた!! 🎉
次の日本語を英訳したときの()に当てはまる単語を選べ。
「がんばれカカロット お前がナンバーワンだ!」
Hang in there Kakarot, you are () one!
① only ② number ③ minus ④ another
問題文読み上げでやったことまとめ 📖
Lexiconを作成
スペースを
<break>
タグにフリガナを
<phoneme>
タグに日英の混ざった文章対応
Amazon Pollyの補足情報 🔍
2種類の音声
標準音声 と ニューラル音声 がある
ニューラル音声の方がよりいい感じ
対応している声が異なる
ニューラル音声の方がお高い
参考: Amazon Polly の音声
2種類の音声
複数の問題文を 2種類の音声 で生成
聞き比べ用HTML を作成しブラウザで確認
同期処理の 文字数制限
synthesize_speech()
は 文字数制限 ありstart_speech_synthesis_task()
で長文を 非同期 処理結果はS3に保存
get_speech_synthesis_task()
で 状態 を取得
数式読み上げ
問題文では mathjaxで数式 を描画
svgになっているため読み上げできない
mathjaxが出力するMathMLを読み上げるといけるかも?→現在調査中
まとめ 📚
Amazon Pollyで音声合成は 簡単 にできる
多言語 に対応
SSML、Lexicon で細かい調整が可能
ぜひ試してみてください
サンプルコード: code
Thank You 🙏
@takanory takanory takanory takanory