スライドを音声入りビデオにして送れ、と言われたので試してみたかった音声合成を試してみた。まあ、どう考えても私の英語よりは聞きやすいだろう。
Amazon Polly
Amazon Polly は、AWSの音声サービス。コンソールから使うこともできるが、AWSなので、APIでも使うことができる。関係ないけどAWSのサービスって、AWS XXX という場合と Amazon XXX という場合があるような気がするけど、どうやって使い分けてるんだろう。PollyにはAWSではなくAmazonがついている。
使い方
-
AWS アカウントを用意する
-
権限としてPollyFullAccess のみを付与した Polly専用IAMを作り、ACCESS KEYと SECRET KEYを取得する。
ルートアカウントで、ACCESS KEYを作ることももちろんできるが、まあ絶対やめたほうがいい。言うまでもないが、漏洩した場合のダメージを最小化するため。 -
~/.aws/credentials
にこれらの値を書く。[default] aws_access_key_id = XXXXX aws_secret_access_key = XXXXX
-
~/.aws/config
に下のようにリージョンを指定する。[default] region=us-east-1
-
python3 と boto3 をインストールする。
> pip install boto3
コード
使い方は思いの外簡単で、 ミニマルにはこんな感じ。
import boto3
client = boto3.client('polly')
response = client.synthesize_speech(
OutputFormat='mp3',
SampleRate='8000',
Text='こんにちは、アマゾン ポリー です',
TextType='text',
VoiceId='Mizuki',
)
b = response['AudioStream'].read()
with open('test.mp3', "wb") as f:
f.write(b)
VoiceIdとしてMizuki
を選んでいるが、Idを指定することで、暗黙裡に言語と男性/女性を指定したことになっている。VoiceIdのリストは下のサイトにある。
TextとSSML
テキストで直接指定すると、話す速度を変更したり、文の一部を強調したり、ちょっと間を置くなどのこまかい制御ができない。これを可能にするにはSSMLというXML形式を使う必要がある。SSMLは標準らしくて、Googleなどの音声合成でも同じものが使えるようだ。方言が色々ありそうだけど。
SSMLを使う際には、TextType
にssml
を指定し、Text
に<speak>
タグで囲まれた文書を使えばいい。
response = client.synthesize_speech(
OutputFormat='mp3',
SampleRate='8000',
Text='<speak>こんにちは、アマゾン ポリー です</speak>',
TextType='ssml',
VoiceId='Mizuki',
)
SSMLを使うと発話速度を調節できる。
AWS Pollyの合成音声はやや速めだと思うので、これが重要になる。
次のように書くと速度が90%になる。
<speak>
<prosody rate="90%">
こんにちは、アマゾン ポリー です
</prosody>
</speak>
スクリプト
スライドの音声を合成するのが目的なので、1つのファイルでまとめて管理したい。ということで、簡単なフォーマットを考えてパーサを書いた。スクリプトは下に示す感じで、--- スライド名
で区切って文章を書く。スライド名はファイル名に使われる。また、1枚のスライドの中で、複数の音声を使うことも多いので、[click]
で区切ると前後を別の音声ファイルとして扱うようにした。また、3つ以上連続した改行があった場合、空白をあけるようにしてある。
--- test1
1枚め
--- test2
2枚め
--- test3
3枚め
[click]
3枚めの2
このようなスクリプトに対して、
generate_mp3("script2.txt", targets=None, voice_id="Mizuki", speech_rate="60%")
のようにすると、test1-1.mp3, test2-1.mp3, test3-1.mp3, test3-2.mp3
の4つのファイルができる。voice-id と発話速度を制御できる。
コードは以下のような感じ。
import boto3
DEFAULT_VOICE_ID="Matthew"
client = boto3.client('polly')
def generate_ssml(text, speech_rate):
text = text.replace("\n\n\n\n", '<break time="2s"/>')
text = text.replace("\n\n\n", '<break time="1.5s"/>')
return '<speak><prosody rate="{}">{}</prosody></speak>'.format(speech_rate, text)
def speak_to_mp3(client, text, filename, voice_id=DEFAULT_VOICE_ID, speech_rate="100%"):
response = client.synthesize_speech(
OutputFormat='mp3',
SampleRate='8000',
Text=generate_ssml(text, speech_rate),
TextType='ssml',
VoiceId=voice_id
)
b = response['AudioStream'].read()
with open(filename, "wb") as f:
f.write(b)
def parse_script(filename, targets=None):
''' read the file. use --- as page separator, [click] as file separator.
targets = "page1, page2" '''
if targets:
targets = [x.strip() for x in targets.split(',')]
print(targets)
d = {} # key: filname, value: text
section_counter = 1
page = None
content = ""
def push_content():
if (targets == None) or (page in targets):
nonlocal content
fn = "{}-{}.mp3".format(page, section_counter)
d[fn] = content
content = ""
with open(filename, "r") as f:
for l in f.readlines():
if l.startswith("---"):
if page:
push_content()
section_counter = 1
page = l.split()[-1]
continue
if l.startswith("[click]"):
push_content()
section_counter += 1
continue
content += l
push_content()
return d
def generate_mp3(script_file, targets=None, voice_id=None, speech_rate="90%"):
d = parse_script(script_file, targets=targets)
for filen, text in d.items():
speak_to_mp3(client, text, filen, voice_id=voice_id, speech_rate=speech_rate)
所感
Polly の生成する音声の質は十分に高い。
生成された音声をPowerPoint に張り込んで、export でmp4ファイルを作成して出来上がり。ただ、アニメーションのタイミングをあわせたりなど、そんなに楽ではない。スライドをビデオにする方法として、全体として楽だったかどうかはかなり疑問かな。。
どうせやるならMarkdownやLatexでスライドを書いて、静止画像と音声データから、直接ビデオを生成するとかやったほうがいいかもしれない。そのうちやってみよう。