Skip to content
Learni
Voir tous les tutoriels
AWS

Comment maîtriser Amazon Polly TTS en 2026

Read in English

Introduction

Amazon Polly, le service TTS d'AWS, a évolué en 2026 avec des voix Neural ultra-réalistes, un support SSML étendu et des lexicons pour une prononciation parfaite. Ce tutoriel expert vous guide pour l'intégrer dans une app Node.js, en partant des bases vers des cas avancés comme le streaming audio et la personnalisation phonétique. Pourquoi c'est crucial ? Les apps vocales (assistants IA, audiobooks, accessibilité) exigent une latence minimale et une qualité humaine. Imaginez convertir du texte dynamique en MP3 fluide en <200ms. Avec 15 ans d'expérience, je partage des configs production-ready : 6 étapes codées, optimisées SEO et scalables. À la fin, votre API TTS surpassera les concurrents open-source. Prêt à vocaliser vos données ? (128 mots)

Prérequis

  • Compte AWS avec permissions PollyFullAccess (IAM policy)
  • Node.js 20+ et npm/yarn
  • Clés AWS : AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION=us-east-1 en variables d'environnement
  • Connaissances avancées en async/await et streams Node.js
  • Outil audio comme VLC pour tester les sorties MP3

Installation et configuration AWS SDK v3

terminal
mkdir polly-expert && cd polly-expert
npm init -y
npm install @aws-sdk/client-polly dotenv
npm install -D @types/node typescript ts-node

cat > .env << EOF
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_REGION=us-east-1
EOF

cat > tsconfig.json << 'EOF'
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "strict": true,
    "esModuleInterop": true
  }
}
EOF

Ce script initialise un projet TypeScript avec AWS SDK v3 pour Polly (modularité optimale). Les vars d'env sécurisent les credentials ; jamais les hardcoder. Piège : Oublier dotenv expose vos clés en prod – utilisez SSM Parameter Store à la place.

Premier appel : Synthèse TTS basique

Avant les features expertes, validons la connexion. Ce code génère un MP3 simple avec une voix standard. Notez OutputFormat.MP3 pour compatibilité web.

Script TTS basique fonctionnel

basic-polly.ts
import { PollyClient, SynthesizeSpeechCommand, OutputFormat } from '@aws-sdk/client-polly';
import * as fs from 'fs';
import * as dotenv from 'dotenv';
dotenv.config();

const client = new PollyClient({ region: process.env.AWS_REGION });

const synthesize = async (text: string) => {
  const input = {
    Text: text,
    OutputFormat: OutputFormat.Mp3,
    VoiceId: 'Joanna',
    Engine: 'standard',
  };
  const command = new SynthesizeSpeechCommand(input);
  const response = await client.send(command);
  if (response.AudioStream) {
    const audioBuffer = await streamToBuffer(response.AudioStream);
    fs.writeFileSync('output.mp3', audioBuffer);
    console.log('✅ Fichier généré : output.mp3');
  }
};

const streamToBuffer = (stream: NodeJS.ReadableStream): Promise<Buffer> =>
  new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    stream.on('data', (chunk) => chunks.push(chunk));
    stream.on('end', () => resolve(Buffer.concat(chunks)));
    stream.on('error', reject);
  });

synthesize('Bonjour, ceci est un test Amazon Polly en 2026.');

// Exécuter : npx ts-node basic-polly.ts

Ce script complet synthétise du texte en MP3 via SynthesizeSpeechCommand. streamToBuffer gère le flux binaire efficacement (mémoire optimisée). Piège : Sans Engine: 'standard', les voix Neural échouent – testez toujours en local.

Niveau avancé : SSML pour prosodie experte

SSML (Speech Synthesis Markup Language) permet de contrôler intonation, pauses et emphase, comme un chef d'orchestre vocal. Exemple : Ralentir sur les chiffres pour clarté.

TTS avec SSML personnalisé

ssml-polly.ts
import { PollyClient, SynthesizeSpeechCommand, OutputFormat, Engine } from '@aws-sdk/client-polly';
import * as fs from 'fs';
import * as dotenv from 'dotenv';
dotenv.config();

const client = new PollyClient({ region: process.env.AWS_REGION });

const synthesizeSSML = async (ssml: string) => {
  const input = {
    Text: ssml,
    TextType: 'ssml',
    OutputFormat: OutputFormat.Mp3,
    VoiceId: 'Mathieu',
    Engine: Engine.Neural,
  };
  const command = new SynthesizeSpeechCommand(input);
  const response = await client.send(command);
  if (response.AudioStream) {
    const audioBuffer = await streamToBuffer(response.AudioStream);
    fs.writeFileSync('ssml-output.mp3', audioBuffer);
    console.log('✅ SSML généré : ssml-output.mp3');
  }
};

const streamToBuffer = (stream: NodeJS.ReadableStream): Promise<Buffer> =>
  new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    stream.on('data', (chunk) => chunks.push(chunk));
    stream.on('end', () => resolve(Buffer.concat(chunks)));
    stream.on('error', reject);
  });

// SSML avancé : emphase, pause, rate
synthesizeSSML(`<speak>
  Bonjour !<break time="500ms"/>
  <prosody rate="slow">Le prix est <emphasis level="strong">99,99 €</emphasis>.</prosody>
  <prosody pitch="high">Parfait pour 2026 !</prosody>
</speak>`);

// Exécuter : npx ts-node ssml-polly.ts

SSML avec Engine.Neural et TextType: 'ssml' active prosodie humaine. , , structurent le discours. Piège : SSML non validé renvoie InvalidSSML – utilisez valideurt SSML AWS.

Lexicons personnalisés pour prononciation

Les lexicons corrigent la phonétique (ex: acronymes, noms propres). Créez-en un via PutLexicon, réutilisable.

Création et usage de lexicon

lexicon-polly.ts
import { PollyClient, PutLexiconCommand, SynthesizeSpeechCommand, OutputFormat, Engine, DeleteLexiconCommand } from '@aws-sdk/client-polly';
import * as fs from 'fs';
import * as dotenv from 'dotenv';
dotenv.config();

const client = new PollyClient({ region: process.env.AWS_REGION });

const lexiconName = 'ExpertLexicon2026';

const createLexicon = async () => {
  const lexicon = {
    Name: lexiconName,
    Content: `<?xml version="1.0" encoding="UTF-8"?>
    <lexicon version="1.0"
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
      alphabet="ipa" xml:lang="fr-FR">
      <lexeme>
        <grapheme>Learni</grapheme>
        <alias>le ar ni</alias>
      </lexeme>
      <lexeme>
        <grapheme>API</grapheme>
        <alias>a p i</alias>
      </lexeme>
    </lexicon>`,
  };
  const command = new PutLexiconCommand(lexicon);
  await client.send(command);
  console.log('✅ Lexicon créé');
};

const synthesizeWithLexicon = async () => {
  const input = {
    Text: 'Learni Dev publie une API Polly en 2026.',
    OutputFormat: OutputFormat.Mp3,
    VoiceId: 'Celine',
    Engine: Engine.Neural,
    LexiconNames: [lexiconName],
  };
  const command = new SynthesizeSpeechCommand(input);
  const response = await client.send(command);
  if (response.AudioStream) {
    const audioBuffer = await streamToBuffer(response.AudioStream);
    fs.writeFileSync('lexicon-output.mp3', audioBuffer);
    console.log('✅ Avec lexicon : lexicon-output.mp3');
  }
};

const cleanup = async () => {
  const delCmd = new DeleteLexiconCommand({ Name: lexiconName });
  await client.send(delCmd);
  console.log('🧹 Lexicon supprimé');
};

const streamToBuffer = (stream: NodeJS.ReadableStream): Promise<Buffer> =>
  new Promise((resolve, reject) => {
    const chunks: Buffer[] = [];
    stream.on('data', (chunk) => chunks.push(chunk));
    stream.on('end', () => resolve(Buffer.concat(chunks)));
    stream.on('error', reject);
  });

await createLexicon();
await synthesizeWithLexicon();
await cleanup();

// Exécuter : npx ts-node lexicon-polly.ts

Lexicon XML en IPA corrige 'Learni' et 'API'. LexiconNames l'applique. Nettoyage auto évite quotas (10 lexicons/région). Piège : Alphabet erroné ('ipa' vs 'x-sampa') mute les mots – validez avec GetLexicon.

Streaming en temps réel pour low-latency

Pour apps live (chatbots), streamez directement vers WebSocket ou

Serveur streaming TTS (Next.js API)

app/api/tts/route.ts
import { PollyClient, SynthesizeSpeechCommand, OutputFormat, Engine } from '@aws-sdk/client-polly';
import { NextRequest, NextResponse } from 'next/server';
import * as dotenv from 'dotenv';
dotenv.config();

const client = new PollyClient({ region: process.env.AWS_REGION as string });

export async function POST(request: NextRequest) {
  try {
    const { text } = await request.json();
    if (!text || text.length > 3000) {
      return NextResponse.json({ error: 'Texte invalide' }, { status: 400 });
    }

    const input = {
      Text: text,
      OutputFormat: OutputFormat.Mp3,
      VoiceId: 'Lea',
      Engine: Engine.Neural,
    };

    const command = new SynthesizeSpeechCommand(input);
    const response = await client.send(command);

    if (response.AudioStream) {
      return new NextResponse(response.AudioStream as any, {
        headers: {
          'Content-Type': 'audio/mpeg',
          'Cache-Control': 'no-cache',
        },
      });
    }
    return NextResponse.json({ error: 'Synthèse échouée' }, { status: 500 });
  } catch (error) {
    console.error(error);
    return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
  }
}

// Usage : POST /api/tts { "text": "Texte à vocaliser" } → stream MP3

Route Next.js 15+ streame directement AudioStream (latence ~150ms). Headers audio/mpeg pour browsers. Piège : Sans try/catch, les quotas Polly (5 req/s) crashent – implémentez rate-limiting avec Upstash Redis.

Intégration S3 pour stockage scalable

Pour audiobooks massifs, uploadez vers S3 au lieu de disques locaux. Utilisez StartSpeechSynthesisTask pour tâches asynchrones longues (>3k chars).

Tâche async S3 avec Polly

s3-task-polly.ts
import { PollyClient, StartSpeechSynthesisTaskCommand, OutputFormat, Engine, GetSpeechSynthesisTaskCommand } from '@aws-sdk/client-polly';
import * as dotenv from 'dotenv';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
dotenv.config();

const polly = new PollyClient({ region: process.env.AWS_REGION });
const s3 = new S3Client({ region: process.env.AWS_REGION });

const bucket = 'your-polly-bucket-2026'; // Créez-le avant

const startTask = async (text: string, outputS3Key: string) => {
  const input = {
    OutputS3BucketName: bucket,
    OutputS3KeyPrefix: outputS3Key,
    Text: text,
    OutputFormat: OutputFormat.Mp3,
    VoiceId: 'Dmitri',
    Engine: Engine.Neural,
  };
  const command = new StartSpeechSynthesisTaskCommand(input);
  const task = await polly.send(command);
  return task.SynthesisTask?.TaskId;
};

const pollTask = async (taskId: string) => {
  while (true) {
    const statusCmd = new GetSpeechSynthesisTaskCommand({ TaskId: taskId! });
    const status = await polly.send(statusCmd);
    if (status.SynthesisTask?.TaskStatus === 'completed') {
      console.log('✅ Tâche terminée, fichier sur S3');
      break;
    } else if (status.SynthesisTask?.TaskStatus === 'failed') {
      throw new Error('Tâche échouée');
    }
    await new Promise(r => setTimeout(r, 2000));
  }
};

const longText = 'Texte très long pour audiobook... (répétez 10k mots)'.repeat(100);
const taskId = await startTask(longText, 'audiobook.mp3');
await pollTask(taskId!);

// Bonus : Télécharger depuis S3 si besoin
// const getCmd = new GetObjectCommand({ Bucket: bucket, Key: 'audiobook.mp3' });
// const s3obj = await s3.send(getCmd);

StartSpeechSynthesisTaskCommand gère textes >3k chars vers S3 (coût réduit). Polling GetSpeechSynthesisTask tracke status. Piège : Bucket sans pubRead crash upload – activez CORS S3 pour web access.

Bonnes pratiques

  • Cachez les audios : Utilisez Redis pour réutiliser synthèses identiques (économie 80% coûts Polly).
  • Choisissez Neural : Voix standard obsolètes ; Neural pour réalisme (mais + cher, testez A/B).
  • Limitez quotas : 5 req/s, 1M chars/jour free tier – implémentez retry exponential avec waiter SDK.
  • Sécurisez SSML : Sanitize inputs pour éviter injections .
  • Monitorez : CloudWatch metrics sur SpeechCharacters pour optimiser billing.

Erreurs courantes à éviter

  • InvalidLexiconId : Lexicon non trouvé – listez avec ListLexiconsCommand avant usage.
  • XRequestLimitExceeded : Pas de rate-limit – utilisez AWS API Gateway throttling.
  • Marks out of range : SSML malformé – validez XML avec lib comme xmldom.
  • Stream non géré : Oublier stream.on('error') leak mémoire – toujours promisify.

Pour aller plus loin