目次

Polly v2.0 完全ガイド(Machine Learning & AI)

概要

Amazon Polly は、テキストを自然な人間らしい音声に変換するテキスト音声合成(TTS: Text-to-Speech)サービスです。Neural TTS(ニューラルネットワークベース)・Standard(統計的合成)・Long-form(長文最適化)・Generative(生成型)の 4 つのエンジンで、60 以上の言語と 100 以上のボイスキャラクターをサポートします。SSML による細かい音声制御(速度・ピッチ・強調)、Newscaster スタイル、Speech Marks による字幕同期、リアルタイム・非同期処理に対応し、アクセシビリティ・オーディオブック・IVR・ゲーム・IoT デバイス向け音声化を実現します。


課題と特徴

従来型 TTS の課題

  • ロボット的な音声: 従来の連結型 TTS では不自然な音声品質
  • 発音・速度・ポーズの制御が難しい: 細かい音声制御が手作業
  • 多言語・多ボイス対応の複雑さ: 言語ごとの音声エンジン構築が必要
  • スケーリングの困難: 大量音声生成のインフラ構築コスト高

Polly の特徴

特徴 効果
Neural TTS 深層学習で人間らしい自然な音声
100+ ボイス 多様なキャラクター・言語対応
SSML サポート 発音・速度・ピッチ・感情を細かく制御
Speech Marks 音声同期・字幕タイミング出力
Newscaster スタイル ニュース向けの定型読上げ
Long-form エンジン 長文コンテンツ対応・イントネーション自動調整
Generative Voice AI 生成音声(実験的)
音声キャッシング 同じ音声の再利用でコスト削減
60+ 言語対応 グローバル対応

アーキテクチャ

graph TB
    subgraph Input["入力"]
        A["テキスト(API)"]
        B["SSML(タグ付き)"]
    end
    
    subgraph Processing["音声合成処理"]
        C["言語検出"]
        D["エンジン選択<br/>Neural/Standard/Long-form"]
        E["SSML 解析"]
        F["発音・速度制御"]
        G["音声生成"]
    end
    
    subgraph Output["出力"]
        I["MP3/PCM/OGG"]
        J["Speech Marks<br/>タイミング情報"]
    end
    
    A --> C
    B --> C
    C --> D
    D --> E
    E --> F
    F --> G
    G --> I
    G --> J

コアコンポーネント

1. Standard TTS(基本合成)

import boto3

polly = boto3.client('polly', region_name='ap-northeast-1')

# シンプルな音声合成
response = polly.synthesize_speech(
    Text='こんにちは。Amazon Polly のデモです。',
    Engine='standard',  # standard/neural/long-form
    LanguageCode='ja-JP',
    OutputFormat='mp3',  # mp3/pcm/ogg_vorbis
    VoiceId='Tomoko',  # 女性音声
    SampleRateHz='8000'  # 8000/16000/22050
)

# 音声ファイルをダウンロード
with open('output.mp3', 'wb') as f:
    f.write(response['AudioStream'].read())

2. Neural TTS(ニューラルネットワーク)

# より自然な音声品質
response = polly.synthesize_speech(
    Text='このサービスは素晴らしいです。強くお勧めします。',
    Engine='neural',  # Neural TTS
    VoiceId='Kazuha',  # 日本語 Neural 音声
    OutputFormat='mp3'
)

3. Long-form TTS(長文対応)

# 長いテキストで自然なイントネーション
long_text = """
電子商取引の発展に伴い、多くの企業がグローバルな市場に進出しています。
特にアジア太平洋地域では、デジタルトランスフォーメーションが急速に進んでいます。
クラウドコンピューティング技術の活用により、企業の競争力が大幅に向上しています。
"""

response = polly.synthesize_speech(
    Text=long_text,
    Engine='long-form',  # Long-form TTS
    VoiceId='Tomoko',
    OutputFormat='mp3'
)

4. SSML(音声制御)

# SSML で細かい音声制御
ssml_text = """
<speak>
    <prosody rate="slow">ゆっくり話します。</prosody>
    <break time="1s"/>
    <prosody volume="loud">大きく話します。</prosody>
    <break time="500ms"/>
    <prosody pitch="high">高い声で話します。</prosody>
    <break time="500ms"/>
    <emphasis level="strong">重要な部分</emphasis>です。
    <prosody rate="fast">速く話します。</prosody>
</speak>
"""

response = polly.synthesize_speech(
    Text=ssml_text,
    Engine='neural',
    LanguageCode='ja-JP',
    OutputFormat='mp3',
    TextType='ssml'  # 'text' or 'ssml'
)

5. Speech Marks(字幕同期情報)

# 音声との同期タイミング情報を取得
response = polly.synthesize_speech(
    Text='これはテストです。',
    Engine='neural',
    VoiceId='Kazuha',
    OutputFormat='json',  # json で Speech Marks 取得
    SpeechMarkTypes=['word', 'sentence', 'viseme'],  # 単語/文/視覚音素
    IncludeExternalPlaylist=True
)

# 出力例
"""
{
  "time": 0,
  "type": "sentence",
  "start": 0,
  "end": 9,
  "value": "これはテストです。"
}
{
  "time": 100,
  "type": "word",
  "start": 0,
  "end": 2,
  "value": "これは"
}
"""

6. 非同期タスク処理(長文・大量)

# 1500 文字超は非同期処理
response = polly.start_speech_synthesis_task(
    Text='長いテキストコンテンツ...',
    Engine='long-form',
    VoiceId='Tomoko',
    LanguageCode='ja-JP',
    OutputFormat='mp3',
    OutputS3BucketName='my-bucket',
    OutputS3KeyPrefix='synthesized-speech/',
    SnsTopicArn='arn:aws:sns:ap-northeast-1:123456789:polly-complete'
)

task_id = response['SynthesisTask']['TaskId']

# タスク完了を確認
while True:
    result = polly.get_speech_synthesis_task(TaskId=task_id)
    if result['SynthesisTask']['TaskStatus'] == 'Completed':
        print(f"✓ 音声合成完了")
        print(f"出力URI: {result['SynthesisTask']['OutputUri']}")
        break

7. Newscaster スタイル(ニュース読上げ)

# ニュース記事向けの定型読上げ
news_text = """
本日、新型技術が発表されました。
この技術は業界に革命をもたらす見通しです。
詳しくは公式サイトをご覧ください。
"""

response = polly.synthesize_speech(
    Text=news_text,
    Engine='neural',
    VoiceId='Mizuki',  # Newscaster 対応音声
    LanguageCode='ja-JP',
    OutputFormat='mp3',
    SpeechMarkTypes=['sentence']
)

8. 言語別音声一覧

# 利用可能な音声を取得
response = polly.describe_voices(LanguageCode='ja-JP')

for voice in response['Voices']:
    print(f"""
    音声名: {voice['Name']}
    性別: {voice['Gender']}
    言語: {voice['LanguageName']}
    エンジン対応: {voice['SupportedEngines']}
    """)

# 出力例
"""
音声名: Kazuha
性別: Female
言語: Japanese
エンジン対応: ['neural', 'standard']
"""

主要ユースケース

1. アクセシビリティ機能(視覚障害者向け)

import boto3

class AccessibleWebContent:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.s3 = boto3.client('s3')
    
    def make_webpage_accessible(self, webpage_html, webpage_id):
        """Web サイトをアクセシブルにするため音声化"""
        
        # HTML からテキスト抽出
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(webpage_html, 'html.parser')
        text_content = soup.get_text()
        
        # Polly で音声化
        response = self.polly.start_speech_synthesis_task(
            Text=text_content,
            Engine='neural',
            VoiceId='Kazuha',
            LanguageCode='ja-JP',
            OutputFormat='mp3',
            OutputS3BucketName='my-bucket',
            OutputS3KeyPrefix=f'accessible-audio/{webpage_id}/'
        )
        
        # Speech Marks で字幕同期情報取得
        marks_response = self.polly.synthesize_speech(
            Text=text_content,
            Engine='neural',
            VoiceId='Kazuha',
            OutputFormat='json',
            TextType='text',
            SpeechMarkTypes=['word', 'sentence']
        )
        
        return {
            'audio_uri': response['SynthesisTask']['OutputUri'],
            'speech_marks': marks_response['AudioStream'].read()
        }

2. オーディオブック・ポッドキャスト生成

class AudiobookGenerator:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.dynamodb = boto3.resource('dynamodb')
        self.books_table = self.dynamodb.Table('Books')
    
    def generate_audiobook(self, book_id, book_content, target_language='ja'):
        """テキスト本からオーディオブックを自動生成"""
        
        # 章ごとに分割
        chapters = self._split_into_chapters(book_content)
        
        chapter_audios = []
        
        for chapter_num, chapter_text in enumerate(chapters):
            # 長文用 Long-form エンジン
            response = self.polly.start_speech_synthesis_task(
                Text=chapter_text,
                Engine='long-form',
                VoiceId='Kazuha',
                LanguageCode=f'{target_language}-JP',
                OutputFormat='mp3',
                OutputS3BucketName='audiobooks-bucket',
                OutputS3KeyPrefix=f'{book_id}/chapter-{chapter_num+1}/'
            )
            
            chapter_audios.append({
                'chapter': chapter_num + 1,
                'task_id': response['SynthesisTask']['TaskId'],
                'status': 'processing'
            })
        
        # DynamoDB に保存
        self.books_table.update_item(
            Key={'book_id': book_id},
            UpdateExpression='SET audio_chapters = :chapters, audiobook_status = :status',
            ExpressionAttributeValues={
                ':chapters': chapter_audios,
                ':status': 'audio_generating'
            }
        )
        
        return chapter_audios

3. IVR(音声自動応答)システム

import asyncio
import boto3

class IVRSystem:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.connect = boto3.client('connect')
    
    def generate_ivr_prompts(self):
        """コールセンター IVR プロンプトの動的生成"""
        
        prompts = {
            'welcome': 'いらっしゃいませ。お電話ありがとうございます。',
            'queue_time': 'ただいま 3 名様が対応中です。お待ち時間は約 2 分です。',
            'transfer': 'サポート部門にお繋ぎいたします。',
            'goodbye': 'ご利用ありがとうございました。'
        }
        
        for prompt_key, prompt_text in prompts.items():
            # リアルタイムで SSML 含む動的メッセージ生成可能
            ssml_text = f"""
            <speak>
                <prosody rate="medium" pitch="0%">
                    {prompt_text}
                </prosody>
            </speak>
            """
            
            response = self.polly.synthesize_speech(
                Text=ssml_text,
                Engine='neural',
                VoiceId='Tomoko',
                OutputFormat='pcm',  # PCM で低遅延
                SampleRateHz='8000',  # 8kHz で電話品質
                TextType='ssml'
            )
            
            # Lambda を通じて Amazon Connect に返す
            yield {
                'prompt_key': prompt_key,
                'audio_stream': response['AudioStream'].read()
            }

4. ゲーム・キャラクター音声

class GameCharacterVoices:
    def __init__(self):
        self.polly = boto3.client('polly')
    
    def generate_game_dialogue(self, character_name, dialogue_text, character_emotion='neutral'):
        """ゲームキャラクターの音声セリフを生成"""
        
        # キャラクターごとの音声・感情スタイル設定
        voice_config = {
            'princess': {'voice': 'Mizuki', 'rate': 'medium', 'pitch': '+10%'},
            'villain': {'voice': 'Tomoko', 'rate': 'slow', 'pitch': '-5%'},
            'hero': {'voice': 'Kazuha', 'rate': 'medium', 'pitch': '+5%'},
        }
        
        config = voice_config.get(character_name, {})
        
        # SSML で感情表現
        emotion_mapping = {
            'happy': '<prosody rate="fast" pitch="+15%">',
            'sad': '<prosody rate="slow" pitch="-10%">',
            'angry': '<prosody rate="fast" volume="loud">',
            'neutral': '<prosody rate="medium">',
        }
        
        ssml_text = f"""
        <speak>
            {emotion_mapping.get(character_emotion, emotion_mapping['neutral'])}
                {dialogue_text}
            </prosody>
        </speak>
        """
        
        response = self.polly.synthesize_speech(
            Text=ssml_text,
            Engine='neural',
            VoiceId=config.get('voice', 'Kazuha'),
            OutputFormat='ogg_vorbis',  # ゲームエンジン互換
            TextType='ssml'
        )
        
        return response['AudioStream'].read()

5. 多言語教育コンテンツ

class LanguageLearningContent:
    def __init__(self):
        self.polly = boto3.client('polly')
    
    def generate_pronunciation_examples(self, word, languages=['en', 'ja', 'es', 'fr']):
        """学習者向けに複数言語の発音例を生成"""
        
        pronunciations = {}
        
        for language in languages:
            # 言語ごとの音声で発音確認
            language_code = self._get_language_code(language)
            
            response = self.polly.synthesize_speech(
                Text=word,
                Engine='neural',
                LanguageCode=language_code,
                OutputFormat='mp3'
            )
            
            pronunciations[language] = response['AudioStream'].read()
        
        return pronunciations
    
    def _get_language_code(self, lang_code):
        mapping = {
            'en': 'en-US',
            'ja': 'ja-JP',
            'es': 'es-ES',
            'fr': 'fr-FR',
        }
        return mapping.get(lang_code, 'en-US')

6. IoT デバイス音声通知

import boto3

def iot_device_notification(device_id, message, device_language='ja'):
    """IoT デバイスに音声で通知を配信"""
    
    polly = boto3.client('polly')
    iot_data = boto3.client('iot-data')
    
    # Polly で音声生成(低遅延は PCM 8kHz)
    response = polly.synthesize_speech(
        Text=message,
        Engine='standard',  # 速度重視
        VoiceId='Tomoko' if device_language == 'ja' else 'Joanna',
        OutputFormat='pcm',
        SampleRateHz='8000'  # IoT デバイス用
    )
    
    # IoT デバイスに音声データを送信
    iot_data.publish(
        topic=f'devices/{device_id}/audio-notification',
        qos=1,
        payload=response['AudioStream'].read()
    )

7. 映像字幕の音声付与

from moviepy.editor import VideoFileClip, CompositeAudioFileClip

def add_voiceover_to_video(video_path, subtitle_text, output_path):
    """ビデオに音声付きナレーション追加"""
    
    polly = boto3.client('polly')
    
    # Polly で音声ナレーション生成
    response = polly.synthesize_speech(
        Text=subtitle_text,
        Engine='neural',
        VoiceId='Kazuha',
        OutputFormat='mp3'
    )
    
    # 音声をファイルに保存
    import tempfile
    with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
        f.write(response['AudioStream'].read())
        audio_file = f.name
    
    # MoviePy でビデオに音声合成
    video = VideoFileClip(video_path)
    from pydub import AudioSegment
    audio = AudioSegment.from_mp3(audio_file)
    
    # 字幕・音声合成してビデオ出力
    # ...処理省略

8. 通知・アラート音声化

class AlertVoiceNotifier:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.sns = boto3.client('sns')
    
    def send_voice_alert(self, alert_type, alert_message):
        """アラートを音声で通知"""
        
        # アラートタイプに応じた音声スタイル
        alert_styles = {
            'critical': {
                'text': f'<speak><prosody volume="loud" rate="fast">{alert_message}</prosody></speak>',
                'engine': 'neural'
            },
            'warning': {
                'text': f'<speak><prosody rate="medium">{alert_message}</prosody></speak>',
                'engine': 'standard'
            },
            'info': {
                'text': alert_message,
                'engine': 'standard'
            }
        }
        
        style = alert_styles.get(alert_type, alert_styles['info'])
        
        response = self.polly.synthesize_speech(
            Text=style['text'],
            Engine=style['engine'],
            VoiceId='Tomoko',
            OutputFormat='mp3',
            TextType='ssml' if 'speak' in style['text'] else 'text'
        )
        
        # SNS で音声メッセージ配信
        self.sns.publish(
            TopicArn='arn:aws:sns:ap-northeast-1:123456789:alerts',
            Message='Voice alert',
            MessageStructure='json',
            MessageAttributes={
                'audio_content': {
                    'DataType': 'Binary',
                    'StringValue': response['AudioStream'].read().hex()
                }
            }
        )

9. リアルタイムカスタマー対応(Lex 統合)

import boto3

class LexVoiceBot:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.lex = boto3.client('lex-runtime')
    
    async def voice_chat(self, user_input, session_id):
        """Lex + Polly でボイスチャット実現"""
        
        # Step 1: Lex で自然言語理解
        lex_response = self.lex.post_text(
            botName='MyBot',
            botAlias='PROD',
            userId=session_id,
            inputText=user_input
        )
        
        bot_response = lex_response['message']
        
        # Step 2: Polly でボット応答を音声化
        polly_response = self.polly.synthesize_speech(
            Text=bot_response,
            Engine='neural',
            VoiceId='Kazuha',
            OutputFormat='mp3'
        )
        
        return {
            'text_response': bot_response,
            'audio_response': polly_response['AudioStream'].read()
        }

10. マルチモーダル学習教材

class InteractiveEducationPlatform:
    def __init__(self):
        self.polly = boto3.client('polly')
        self.rekognition = boto3.client('rekognition')
    
    def generate_interactive_lesson(self, lesson_content, images):
        """画像 + テキスト + 音声の統合学習教材"""
        
        lesson_data = {}
        
        for section_id, (text, image) in enumerate(zip(lesson_content, images)):
            # Step 1: 画像を Rekognition で分析
            image_labels = self.rekognition.detect_labels(Image={'S3Object': image})
            
            # Step 2: テキストに画像情報を組み込み
            enhanced_text = f"""
            {text}
            
            この画像には以下の要素が含まれています:
            {', '.join([label['Name'] for label in image_labels['Labels'][:3]])}
            """
            
            # Step 3: Polly で音声化
            audio_response = self.polly.synthesize_speech(
                Text=enhanced_text,
                Engine='long-form',
                VoiceId='Kazuha',
                OutputFormat='mp3'
            )
            
            lesson_data[f'section_{section_id}'] = {
                'text': enhanced_text,
                'image': image,
                'audio': audio_response['AudioStream'].read(),
                'labels': image_labels['Labels']
            }
        
        return lesson_data

設定・操作の具体例

CLI 例(5+)

1. シンプルな音声合成

aws polly synthesize-speech \
  --text 'こんにちは。テストです。' \
  --voice-id 'Kazuha' \
  --engine 'neural' \
  --language-code 'ja-JP' \
  --output-format 'mp3' \
  --region 'ap-northeast-1' \
  output.mp3

2. SSML で音声制御

aws polly synthesize-speech \
  --text '<speak><prosody rate="slow">ゆっくり</prosody> <break time="1s"/> <prosody pitch="+10%">高い声</prosody></speak>' \
  --text-type 'ssml' \
  --voice-id 'Tomoko' \
  --engine 'neural' \
  --output-format 'mp3' \
  --region 'ap-northeast-1' \
  output.mp3

3. Speech Marks 取得

aws polly synthesize-speech \
  --text 'これはテストです。' \
  --voice-id 'Kazuha' \
  --engine 'neural' \
  --output-format 'json' \
  --speech-mark-types 'word' 'sentence' \
  --region 'ap-northeast-1' \
  output.json

4. 非同期タスク開始

aws polly start-speech-synthesis-task \
  --text 'これは長いテキストです。非同期で処理されます。' \
  --engine 'long-form' \
  --voice-id 'Kazuha' \
  --language-code 'ja-JP' \
  --output-format 'mp3' \
  --output-s3-bucket-name 'my-bucket' \
  --output-s3-key-prefix 'synthesized/' \
  --region 'ap-northeast-1'

5. 利用可能な音声一覧

aws polly describe-voices \
  --language-code 'ja-JP' \
  --region 'ap-northeast-1' \
  --query 'Voices[*].[Name,Gender,SupportedEngines]' \
  --output table

SDK 例(5+)

1. Python: SSML + Speech Marks

import boto3
import json

polly = boto3.client('polly')

ssml = """
<speak>
    <p>段落1です。</p>
    <p>段落2です。</p>
</speak>
"""

response = polly.synthesize_speech(
    Text=ssml,
    TextType='ssml',
    Engine='neural',
    VoiceId='Kazuha',
    OutputFormat='json',
    SpeechMarkTypes=['sentence', 'word', 'viseme'],
    LanguageCode='ja-JP'
)

# Speech Marks を JSON で出力
marks_data = response['AudioStream'].read()
print(json.loads(marks_data))

2. JavaScript: 非同期処理

const AWS = require('aws-sdk');
const polly = new AWS.Polly({ region: 'ap-northeast-1' });

async function generateAudiobook(bookText) {
    const params = {
        Text: bookText,
        Engine: 'long-form',
        VoiceId: 'Kazuha',
        LanguageCode: 'ja-JP',
        OutputFormat: 'mp3',
        OutputS3BucketName: 'my-bucket',
        OutputS3KeyPrefix: 'audiobooks/',
        SnsTopicArn: 'arn:aws:sns:ap-northeast-1:123456789:polly-complete'
    };
    
    try {
        const result = await polly.startSpeechSynthesisTask(params).promise();
        console.log(`Task started: ${result.SynthesisTask.TaskId}`);
        console.log(`Status: ${result.SynthesisTask.TaskStatus}`);
    } catch (error) {
        console.error('Error:', error);
    }
}

3. Go: 複数ボイスで生成

package main

import (
    "fmt"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/polly"
)

func synthesizeMultipleVoices(text string, voices []string) {
    sess := session.Must(session.NewSession(&aws.Config{
        Region: aws.String("ap-northeast-1"),
    }))
    
    svc := polly.New(sess)
    
    for _, voiceId := range voices {
        result, _ := svc.SynthesizeSpeech(&polly.SynthesizeSpeechInput{
            Text:       aws.String(text),
            VoiceId:    aws.String(voiceId),
            Engine:     aws.String("neural"),
            OutputFormat: aws.String("mp3"),
        })
        
        fmt.Printf("Generated audio for voice: %s\n", voiceId)
        // 音声ファイルに保存
    }
}

4. Java: PCM 形式で IVR 最適化

import software.amazon.awssdk.services.polly.PollyClient;
import software.amazon.awssdk.services.polly.model.*;

public class IVRPromptGenerator {
    public static void main(String[] args) {
        PollyClient pollyClient = PollyClient.builder()
            .region(Region.AP_NORTHEAST_1)
            .build();
        
        SynthesizeSpeechRequest request = SynthesizeSpeechRequest.builder()
            .text("お電話ありがとうございます。")
            .voiceId("Tomoko")
            .engine(Engine.NEURAL)
            .outputFormat(OutputFormat.PCM)
            .sampleRateHz("8000")  // 電話品質
            .build();
        
        SynthesizeSpeechResponse response = pollyClient.synthesizeSpeech(request);
        
        // PCM データを処理
        pollyClient.close();
    }
}

5. C#: 非同期タスク管理

using Amazon;
using Amazon.Polly;
using Amazon.Polly.Model;
using System;
using System.Threading.Tasks;

class AudiobookGenerator {
    static async Task Main() {
        var client = new AmazonPollyClient(RegionEndpoint.APNortheast1);
        
        var request = new StartSpeechSynthesisTaskRequest {
            Text = "長いテキストのオーディオブック生成...",
            Engine = Engine.LongForm,
            VoiceId = "Kazuha",
            LanguageCode = "ja-JP",
            OutputFormat = OutputFormat.Mp3,
            OutputS3BucketName = "my-bucket",
            OutputS3KeyPrefix = "audiobooks/",
            SnsTopicArn = "arn:aws:sns:ap-northeast-1:123456789:polly"
        };
        
        var response = await client.StartSpeechSynthesisTaskAsync(request);
        Console.WriteLine({{CONTENT}}quot;Task ID: {response.SynthesisTask.TaskId}");
        Console.WriteLine({{CONTENT}}quot;Status: {response.SynthesisTask.TaskStatus}");
    }
}

IaC 例(5+)

1. CloudFormation: Polly 用 IAM ロール

Resources:
  PollyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: polly.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: PollyS3Access
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                Resource: 'arn:aws:s3:::my-bucket/*'

  PollyOutputBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: polly-output-${AWS::AccountId}

2. Terraform: SNS トピック統合

resource "aws_s3_bucket" "polly_output" {
  bucket = "polly-output-${data.aws_caller_identity.current.account_id}"
}

resource "aws_sns_topic" "polly_completion" {
  name = "polly-task-completion"
}

resource "aws_sns_topic_subscription" "polly_webhook" {
  topic_arn = aws_sns_topic.polly_completion.arn
  protocol  = "https"
  endpoint  = "https://example.com/polly-webhook"
}

resource "aws_iam_role" "polly_async_role" {
  name = "polly-async-task-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "polly.amazonaws.com"
      }
    }]
  })
}

3. CDK (Python): オーディオブック生成パイプライン

from aws_cdk import (
    aws_s3 as s3,
    aws_lambda as lambda_,
    aws_sns as sns,
    aws_iam as iam,
    core
)

class PollyAudiobookStack(core.Stack):
    def __init__(self, scope: core.Construct, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)
        
        # S3 バケット
        output_bucket = s3.Bucket(self, "AudiobookOutput")
        
        # SNS トピック
        completion_topic = sns.Topic(
            self, "PollyCompletion",
            display_name="Polly Task Completion"
        )
        
        # Lambda: Polly タスク監視
        monitor_function = lambda_.Function(
            self, "PollyMonitor",
            runtime=lambda_.Runtime.PYTHON_3_11,
            code=lambda_.Code.from_asset("lambda"),
            handler="monitor.handler",
            environment={
                "SNS_TOPIC": completion_topic.topic_arn,
                "OUTPUT_BUCKET": output_bucket.bucket_name
            }
        )

4. CloudFormation: Lambda + Polly 統合

Resources:
  PollyLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.11
      Handler: index.handler
      Role: !GetAtt LambdaRole.Arn
      Code:
        ZipFile: |
          import boto3
          polly = boto3.client('polly')
          
          def handler(event, context):
              response = polly.synthesize_speech(
                  Text='Lambda から Polly へのテスト',
                  VoiceId='Kazuha',
                  Engine='neural',
                  OutputFormat='mp3'
              )
              return {'statusCode': 200}

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

5. Terraform: Lambda + EventBridge トリガー

resource "aws_lambda_function" "polly_tts" {
  filename = "polly_lambda.zip"
  function_name = "text-to-speech-processor"
  role          = aws_iam_role.lambda_role.arn
  handler       = "index.handler"
  runtime       = "python3.11"
  timeout       = 300
}

resource "aws_events_rule" "text_upload" {
  name = "detect-text-file-upload"
  
  event_pattern = jsonencode({
    source = ["aws.s3"]
    detail = {
      bucket = {
        name = ["text-input-bucket"]
      }
    }
  })
}

resource "aws_events_target" "polly_target" {
  rule = aws_events_rule.text_upload.name
  arn  = aws_lambda_function.polly_tts.arn
}

比較表

特性 Polly ElevenLabs Google TTS Azure Speech OpenAI TTS Murf WellSaid
言語対応 60+ 29 100+ 200+ 26 20+ 10+
ボイス数 100+ 1000+ 140+ 200+ 6 130+ 270+
Neural/生成
SSML制御
Speech Marks
非同期処理
IVR最適化
無料枠 100万文字/月 10,000文字 無し 50万文字/月 無し トライアル トライアル
料金 $4/100万文字 $5-30/100万文字 $5-16/100万文字 $4-16/100万文字 $0.015/1k入力 $10+/月 要相談

ベストプラクティス

✅ すべき事

  • エンジン選択: リアルタイム IVR は Standard、オーディオブックは Long-form
  • SSML 活用: 速度・ピッチ・感情を細かく制御
  • Speech Marks 取得: 字幕同期が必要な場合は必須
  • キャッシング活用: 同じテキスト・ボイスの再生成は避ける
  • 音声フォーマット最適化: IVR は PCM 8kHz、ストリーミングは MP3
  • エラーハンドリング: 合成失敗時のフォールバック
  • CloudWatch 監視: 非同期タスク完了を SNS で追跡
  • 多言語テスト: 言語ごとに音声品質を確認
  • アクセシビリティ: 視覚障害者向けに常に音声オプション提供

❌ してはいけない事

  • リアルタイムで Long-form 使用: レイテンシー増大・コスト増加
  • SSML なしで速度制御: CLI パラメータでは速度制御不可
  • 大量テキストをリアルタイム API で: 非同期タスク (start_speech_synthesis_task) を使用
  • 出力フォーマット誤選択: オーディオブックを PCM で生成しない
  • 言語コード未指定: 言語自動検出は精度が低い
  • Speech Marks なしで字幕作成: タイミング情報がないと字幕がずれる
  • キャッシング無視: 同じテキストを何度も合成しない
  • SampleRateHz 無視: デバイスに合った SampleRate を指定
  • エラーログなし: 失敗理由が分からない

トラブルシューティング

症状 原因 解決策
「InvalidParameterException」エラー ボイス/言語コード不正 公式サイトでサポート言語・ボイス確認
音声品質が不自然 エンジン選択誤り Neural エンジン使用、SSML で調整
Speech Marks タイムスタンプ不正 出力フォーマット誤り –output-format を json に設定
非同期タスク失敗 S3 権限不足 IAM ロールに S3 FullAccess 確認
IVR 音声が遅い レイテンシー高い OutputFormat を PCM、SampleRateHz を 8000 に
SSML が反映されない TextType が ‘text’ –text-type を ‘ssml’ に設定
言語サポート外エラー 言語コード未対応 言語サポート一覧確認
キャッシング参照エラー 音声ファイル不在 CloudFront キャッシュを確認
SSML フォーマットエラー XML が不正 SSML バリデータで確認
コスト急増 無駄な再生成 キャッシング実装・不要テキスト削除

2025-2026最新動向

1. Generative Voice(生成型音声)

  • 新規ボイス合成・個性的な音声生成
  • ブランド固有音声の自動生成

2. Real-time TTS Streaming

  • WebSocket での低遅延音声ストリーミング
  • ライブ翻訳・ライブ字幕対応

3. Emotion-Aware TTS

  • テキスト内容に応じた自動感情制御
  • マーケティング・コンテンツの最適化

4. Multi-Speaker Synthesis

  • 複数キャラクターが対話する音声生成
  • ドラマ・ポッドキャスト制作の自動化

5. Custom Voice Training

  • 企業ブランド音声の学習・生成
  • ボイスアクターの再現

学習リソース・参考文献

公式ドキュメント(8+)

  1. Amazon Polly Developer Guide
  2. Supported Languages & Voices
  3. SSML Reference
  4. Speech Marks
  5. API Reference
  6. Pricing
  7. Asynchronous Processing
  8. Security & Compliance

OSS・ベンダー リソース(5+)

  1. pyttsx3 - Offline TTS
  2. gTTS - Google Text-to-Speech
  3. ElevenLabs Python SDK
  4. Mozilla TTS
  5. Tacotron 2 - Open-source TTS

実装例・チェックリスト

まとめ

Polly は 「テキストを自然な音声に変換する TTS サービス」。Neural・Long-form・Generative 4 つのエンジンで 60+ 言語・100+ ボイスに対応し、SSML・Speech Marks で細かい制御が可能。IVR・オーディオブック・アクセシビリティ・ゲーム・IoT の音声化基盤として、Transcribe・Translate・Comprehend と組み合わせた完全な多言語音声パイプラインを構築できます。


最終更新:2026-04-26 バージョン:v2.0