video-audio-design

Par mkurman · zorai

Utilisez cette skill lors de l'ajout d'audio à des vidéos programmatiques : génération de narration avec ElevenLabs TTS, sourcing de musique de fond libre de droits, création de SFX avec FFmpeg, implémentation du ducking audio, ou mixage de plusieurs couches audio dans Remotion. Se déclenche sur ElevenLabs, text-to-speech, génération de voix, musique de fond, effets sonores, mixage audio et ducking de volume.

npx skills add https://github.com/mkurman/zorai --skill video-audio-design

Principes clés

  1. Architecture audio en couches - Chaque vidéo comporte trois couches audio : narration en haut (la plus forte), SFX au milieu (volume d'accentuation), et musique de fond à la base (la plus faible).

  2. La narration détermine le timing - Générez la narration d'abord, mesurez sa durée, puis adaptez le timing des scènes. Ne forcez jamais la narration dans des durées de scènes arbitraires.

  3. Baisser la musique pendant la parole - La musique de fond doit baisser de 50-60% quand la narration joue. Utilisez des transitions fluides (10-15 images) pour éviter les à-coups.

  4. SFX comme accents, pas distractions - Gardez les SFX courts (moins de 0,5s), subtils en volume, et pertinents par rapport à l'action à l'écran.

  5. Tester l'audio en contexte - Prévisualisez toujours le mélange complet avec toutes les couches ensemble. Écoutez la parole boueuse, les pics de volume, ou les silences morts.


Concepts fondamentaux

Architecture audio en 3 couches

Couche Rôle Volume de base Pendant la narration
Narration Transmet l'information, détermine le rythme 0,8-1,0 N/A (couche supérieure)
SFX Accentue les transitions et actions 0,3-0,5 0,3-0,5 (inchangé)
Musique de fond Établit la tonalité émotionnelle, remplit les silences 0,3-0,5 0,15-0,25 (baissée)

Modèle API ElevenLabs

ElevenLabs fournit du TTS neuronal via une API REST. Le flux principal :

  1. Choisissez une voix (prédéfinie ou clonée) - chacune a un voice_id
  2. Envoyez le texte + paramètres de voix à /v1/text-to-speech/{voice_id}
  3. Recevez des bytes audio bruts (mp3 par défaut)
  4. Écrivez dans un fichier et mesurez la durée pour le timing des scènes

Paramètres de voix :

Paramètre Plage Bas Haut Recommandé
stability 0-1 Plus expressif, variable Plus cohérent, monocorde 0,4-0,6
similarity_boost 0-1 Plus créatif Plus proche de la voix originale 0,6-0,8
style 0-1 Livraison neutre Style exagéré 0,3-0,6

Concept du ducking audio

Le ducking audio réduit le volume de la musique de fond quand la narration commence et le restaure quand la narration se termine. Dans Remotion, utilisez interpolate() :

Volume musique :  0,4 ---\              /--- 0,4
                          \            /
               0,15        \__________/
                     début narration → fin

Les transitions doivent prendre 10-15 images (~0,3-0,5s à 30fps).

Synchronisation audio basée sur les images dans Remotion

  • useCurrentFrame() retourne le numéro d'image actuel
  • interpolate() mappe des plages d'images à des plages de valeurs (ex. volume)
  • <Sequence from={frame}> place l'audio à une image spécifique
  • <Audio volume={fn}> accepte un nombre statique ou une fonction par image

Convertissez les secondes en images : images = secondes * fps.


Tâches courantes

1. Configurer la clé API ElevenLabs et générer la narration

import fs from 'fs';

const ELEVENLABS_API_URL = 'https://api.elevenlabs.io/v1';

async function generateNarration(
  text: string,
  voiceId: string,
  outputPath: string
): Promise<void> {
  const response = await fetch(
    `${ELEVENLABS_API_URL}/text-to-speech/${voiceId}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'xi-api-key': process.env.ELEVEN_LABS_API_KEY!,
      },
      body: JSON.stringify({
        text,
        model_id: 'eleven_multilingual_v2',
        voice_settings: {
          stability: 0.5,
          similarity_boost: 0.75,
          style: 0.5,
          use_speaker_boost: true,
        },
      }),
    }
  );

  if (!response.ok) {
    const error = await response.text();
    throw new Error(`ElevenLabs API error ${response.status}: ${error}`);
  }

  const buffer = Buffer.from(await response.arrayBuffer());
  fs.writeFileSync(outputPath, buffer);
}

2. Sélectionner et configurer les paramètres de voix

Questions pour la sélection de voix : genre, tranche d'âge, accent, niveau d'énergie, chaleur.

interface VoiceSettings {
  stability: number;
  similarity_boost: number;
  style: number;
  use_speaker_boost: boolean;
}

const presets: Record<string, VoiceSettings> = {
  explainer: { stability: 0.6, similarity_boost: 0.75, style: 0.4, use_speaker_boost: true },
  promo: { stability: 0.3, similarity_boost: 0.7, style: 0.7, use_speaker_boost: true },
  tutorial: { stability: 0.7, similarity_boost: 0.8, style: 0.2, use_speaker_boost: false },
};

3. Générer la narration par scène à partir d'un script

import { execSync } from 'child_process';
import path from 'path';

interface Scene { id: string; narrationText: string; }
interface SceneWithAudio extends Scene {
  audioPath: string;
  durationMs: number;
  durationFrames: number;
}

function getAudioDurationMs(filePath: string): number {
  const output = execSync(
    `ffprobe -v error -show_entries format=duration -of csv=p=0 "${filePath}"`
  ).toString().trim();
  return Math.round(parseFloat(output) * 1000);
}

async function generateSceneNarrations(
  scenes: Scene[], voiceId: string, outputDir: string, fps: number
): Promise<SceneWithAudio[]> {
  const results: SceneWithAudio[] = [];
  for (const scene of scenes) {
    const audioPath = path.join(outputDir, `${scene.id}.mp3`);
    await generateNarration(scene.narrationText, voiceId, audioPath);
    const durationMs = getAudioDurationMs(audioPath);
    results.push({
      ...scene, audioPath, durationMs,
      durationFrames: Math.ceil((durationMs / 1000) * fps),
    });
  }
  return results;
}

4. Sourcer la musique de fond

Sources de musique libre de droits :

  • Pixabay Audio : https://pixabay.com/music/ (gratuit, pas d'attribution)
  • Freesound : https://freesound.org/ (CC0/CC-BY)
  • YouTube Audio Library : téléchargez depuis YouTube Studio
  • Fichiers locaux : placez dans public/audio/ pour staticFile() de Remotion

5. Générer des SFX avec FFmpeg

# Son de clic - burst sinusoïdale courte
ffmpeg -f lavfi -i "sine=frequency=800:duration=0.05" \
  -af "afade=t=out:st=0.02:d=0.03" click.wav

# Clavier tapé - burst de bruit filtré
ffmpeg -f lavfi -i "anoisesrc=d=0.08:c=white:a=0.3" \
  -af "highpass=f=2000,lowpass=f=8000,afade=t=out:st=0.04:d=0.04" type.wav

# Whoosh - balayage fréquentiel
ffmpeg -f lavfi -i "sine=frequency=200:duration=0.4" \
  -af "vibrato=f=8:d=0.5,afade=t=in:d=0.1,afade=t=out:st=0.2:d=0.2,lowpass=f=1000" \
  whoosh.wav

# Ding/chime - synthèse de cloche
ffmpeg -f lavfi -i "sine=frequency=1200:duration=0.6" \
  -af "afade=t=out:st=0.1:d=0.5,aecho=0.8:0.88:40:0.4" ding.wav

# Pop - impulsion
ffmpeg -f lavfi -i "sine=frequency=400:duration=0.08" \
  -af "afade=t=out:st=0.02:d=0.06,lowpass=f=600" pop.wav

# Transition whoosh
ffmpeg -f lavfi -i "sine=frequency=300:duration=0.3" \
  -af "vibrato=f=12:d=0.8,afade=t=in:d=0.05,afade=t=out:st=0.15:d=0.15,bandpass=f=500:w=400" \
  swoosh.wav

6. Implémenter le ducking audio dans Remotion

import React from 'react';
import { Audio, useCurrentFrame, interpolate, Sequence } from 'remotion';

const AudioMixer: React.FC<{
  narrationSrc: string;
  musicSrc: string;
  narrationStart: number;
  narrationDuration: number;
}> = ({ narrationSrc, musicSrc, narrationStart, narrationDuration }) => {
  const frame = useCurrentFrame();

  const duckRampFrames = 10;
  const musicVolume = interpolate(
    frame,
    [
      narrationStart - duckRampFrames,
      narrationStart,
      narrationStart + narrationDuration,
      narrationStart + narrationDuration + duckRampFrames,
    ],
    [0.4, 0.15, 0.15, 0.4],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
  );

  return (
    <>
      <Audio src={musicSrc} volume={musicVolume} />
      <Sequence from={narrationStart} durationInFrames={narrationDuration}>
        <Audio src={narrationSrc} volume={0.9} />
      </Sequence>
    </>
  );
};

export default AudioMixer;

7. Mixer 3 couches audio dans une composition Remotion

import React from 'react';
import { Audio, Sequence, useCurrentFrame, interpolate } from 'remotion';

interface NarrationSegment { src: string; startFrame: number; durationFrames: number; }
interface SfxEvent { src: string; frame: number; }

const FullAudioMix: React.FC<{
  narrations: NarrationSegment[];
  sfxEvents: SfxEvent[];
  musicSrc: string;
}> = ({ narrations, sfxEvents, musicSrc }) => {
  const frame = useCurrentFrame();
  const duckRamp = 10;

  let musicVolume = 0.4;
  for (const seg of narrations) {
    const duck = interpolate(
      frame,
      [seg.startFrame - duckRamp, seg.startFrame,
       seg.startFrame + seg.durationFrames, seg.startFrame + seg.durationFrames + duckRamp],
      [1, 0.375, 0.375, 1],
      { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
    );
    musicVolume = musicVolume * duck;
  }

  return (
    <>
      <Audio src={musicSrc} volume={musicVolume} loop />
      {sfxEvents.map((sfx, i) => (
        <Sequence key={i} from={sfx.frame} durationInFrames={30}>
          <Audio src={sfx.src} volume={0.4} />
        </Sequence>
      ))}
      {narrations.map((seg, i) => (
        <Sequence key={i} from={seg.startFrame} durationInFrames={seg.durationFrames}>
          <Audio src={seg.src} volume={0.9} />
        </Sequence>
      ))}
    </>
  );
};

export default FullAudioMix;

8. Utiliser des fournisseurs TTS alternatifs

OpenAI TTS - bonne qualité, API simple, six voix intégrées :

import OpenAI from 'openai';
import fs from 'fs';

const openai = new OpenAI();

async function generateWithOpenAI(
  text: string,
  outputPath: string,
  voice: 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer' = 'alloy'
): Promise<void> {
  const mp3 = await openai.audio.speech.create({
    model: 'tts-1-hd',
    voice,
    input: text,
  });
  const buffer = Buffer.from(await mp3.arrayBuffer());
  fs.writeFileSync(outputPath, buffer);
}

Edge TTS - gratuit, nombreuses voix, utilise le service TTS de Microsoft Edge :

pip install edge-tts
edge-tts --voice en-US-AriaNeural --text "Hello world" --write-media output.mp3
edge-tts --list-voices

Anti-patterns / erreurs courantes

Erreur Pourquoi c'est faux Que faire à la place
Musique au même volume pendant la narration La parole devient inintelligible Implémenter le ducking audio - baisser la musique de 50-60% pendant la parole
Coder en dur la clé API ElevenLabs La clé fuit dans le contrôle de version Utiliser des variables d'environnement : process.env.ELEVEN_LABS_API_KEY
Utiliser TTS sans mesurer la durée Timing de scène incorrect, narration coupée Mesurer la durée audio avec ffprobe après la génération
SFX plus fort que la narration Distrait du contenu SFX à 0,3-0,5, narration à 0,8-1,0
Pas de fade sur le début/fin de la musique Le démarrage/arrêt abrupt semble un bug Ajouter 0,5-1s de fade-in au démarrage et fade-out à la fin
Utiliser un modèle TTS de basse qualité La voix robotique undermines la qualité Utiliser eleven_multilingual_v2 ou tts-1-hd
Ignorer le format du fichier audio Certains formats ajoutent du silence de remplissage Utiliser MP3 pour la narration, WAV pour les SFX

Pièges

  1. Limites de débit ElevenLabs et quotas de caractères - Le tier gratuit a une limite de caractères mensuelle. Mettez en cache l'audio généré agressivement et ne régénérez que quand le texte change. Utilisez un hash du texte comme clé de cache.

  2. Le remplissage du codeur MP3 ajoute du silence - Les fichiers MP3 ont souvent 20-50ms de silence au début. Taillez avec ffmpeg -af silenceremove=1:0:-50dB ou compensez le décalage dans le timing des images.

  3. Le volume Remotion Audio est par composant, pas global - Deux composants <Audio> au volume 1,0 peuvent écrêter. Gardez le volume total sur les couches simultanées sous 1,0.

  4. Les SFX FFmpeg sonnent différemment selon les systèmes - Toujours spécifier -ar 44100 -sample_fmt s16 pour une sortie cohérente entre les machines.

  5. Cohérence de voix entre les scènes - ElevenLabs peut produire des tonalités différentes pour les mêmes paramètres avec du texte variable. Utilisez stability >= 0,5 pour la narration multi-scènes.


Références

Pour des patterns détaillés sur des sous-domaines audio spécifiques, consultez le fichier pertinent du dossier references/ :

  • references/elevenlabs-api.md - patterns API ElevenLabs avancés incluant le clonage de voix, TTS streaming, API websocket, dictionnaires de prononciation, et gestion des quotas
  • references/audio-mixing-patterns.md - patterns de mixage avancés incluant le ducking multi-segment, crossfades entre scènes, courbes d'automatisation de volume, et mastérisation du mix final
  • references/sfx-generation.md - génération complète de SFX avec FFmpeg incluant la synthèse complexe, le layering de multiples générateurs, et la construction d'une librairie de SFX réutilisable

Chargez un fichier references seulement si la tâche actuelle le nécessite - ils sont longs et consommeront du contexte.

Skills similaires