Posted in

S’entrainer à sec : créer ses propres tables d’apnée

L’apnée à sec est une pratique essentielle pour les nageurs en monopalme, les apnéistes et même les adeptes de mermaiding. Elle permet d’améliorer la capacité pulmonaire, la gestion du CO₂ et la relaxation, le tout sans même mettre un pied dans l’eau. Mais pour rendre ces séances efficaces et motivantes, rien de tel qu’un accompagnement audio personnalisé.

Aujourd’hui, je te présente un script gratuit, “fait maison”, utilisable via Google Colab, qui te permet de générer des fichiers audio sur mesure pour tes entraînements. Que tu sois débutant ou confirmé, cet outil peut t’aider à structurer tes séances et à progresser en toute sérénité.

Illustration, apnéiste tout équipé sur son canapé

Qu’est-ce que cet outil et comment fonctionne-t-il ?

Développé pour être utilisé directement dans un navigateur, cet outil utilise un notebook Google Colab qui génère des fichiers audio pour guider tes exercices d’apnée à sec.
Voici ses principales fonctionnalités :

  • Personnalisation de la table : tu peux définir la durée des phases d’inspiration, d’expiration, d’apnée et de récupération.
  • Ajout d’une musique de fond : pour rendre l’expérience plus agréable, tu peux intégrer une musique de ton choix (fichier MP3 ou WAV).
  • Génération automatique : l’outil crée un fichier audio prêt à l’emploi, que tu peux télécharger et écouter sur n’importe quel appareil.

L’avantage ? Pas besoin d’installer de logiciel : tout se fait en ligne, gratuitement, et sans compétences techniques avancées. La seule contrainte est d’avoir compte Google.

Exemple de séance type

Pour te donner une idée, voici un exemple de configuration que tu peux tester :

  • Inspiration : 2 minutes
  • Expiration : 1 minute
  • Apnée : 1 minute 30
  • Récupération : 2 minutes
  • Musique de fond : une playlist relaxante ou motivante, selon tes préférences.

Avec ce script, les temps peuvent être adaptés en fonction de ton niveau et de tes objectifs.

Précautions et conseils

En monopalme, la maîtrise de l’apnée est un atout majeur, que ce soit pour les entraînements en piscine ou en compétition.

  • Écoute ton corps : l’apnée à sec doit toujours être pratiquée en conscience. Si tu ressens un inconfort, arrête immédiatement.
  • Commence progressivement : augmente les temps d’apnée petit à petit, sans forcer.

N’hésite pas à l’essayer et à partager tes retours en commentaire ! Et si tu as des idées d’amélioration ou des questions, je suis là pour en discuter.

C’est parti !

Pas besoin d’être un expert en langage Python, suis ces étapes pour créer ton premier fichier audio d’entraînement :

  1. Ouvre le notebook Google Colab : Lien vers l’outil (Clique sur le lien, puis sur « Ouvrir dans Colab » si tu es connecté à ton compte Google.)
  2. Personnalise les paramètres :
    • Dans le notebook, tu trouveras des cellules de code où tu mettras le code proposé ci-dessous.
  3. Exécute le code :
    • Clique sur le bouton « Tout Exécuter » (▶) pour lancer la génération du fichier audio.
    • A l’étape 4, tu pourras aussi uploader une musique de fond.
    • Une fois le traitement terminé, un lien de téléchargement apparaîtra.

Cellule de code 1 :
il permet d’initialiser le notebook avec les bibliothèques qui nous seront utiles.

Python
!apt-get -qq update
!apt-get -qq install -y ffmpeg
!pip -q install gTTS pydub

Cellule de code 2 :
c’est le script proprement dit, enfin sa première partie.
La partie “configuration” te permet de modifier des durées d’exercice.

Python
from __future__ import annotations

from dataclasses import dataclass
from io import BytesIO
from typing import Dict, Iterable, List, Tuple

from gtts import gTTS
from pydub import AudioSegment
from IPython.display import Audio, display


# -------------------------
# Configuration
# -------------------------

@dataclass(frozen=True)
class TrainingConfig:
    duree_preparation_s: int = 60
    duree_apnee_initiale_s: int = 60
    augmentation_s: int = 15
    nombre_cycles: int = 8
    pause_entre_cycles_s: int = 120
    langue: str = "fr"

    # annonces pendant l'apnée
    annonce_toutes_les_s: int = 30
    countdown_final_s: int = 3
    countdown_avant_apnee_s: int = 5

    # petites pauses entre chiffres (sinon ça colle)
    pause_entre_chiffres_ms: int = 250


# -------------------------
# Audio helpers
# -------------------------

def silence(ms: int) -> AudioSegment:
    """Silence safe (jamais négatif)."""
    return AudioSegment.silent(duration=max(0, int(ms)))


class TTS:
    """Synthèse vocale avec cache en mémoire."""

    def __init__(self, lang: str):
        self.lang = lang
        self._cache: Dict[str, AudioSegment] = {}

    def say(self, text: str) -> AudioSegment:
        text = text.strip()
        if not text:
            return silence(0)
        if text in self._cache:
            return self._cache[text]

        mp3_fp = BytesIO()
        gTTS(text=text, lang=self.lang).write_to_fp(mp3_fp)
        mp3_fp.seek(0)
        seg = AudioSegment.from_file(mp3_fp, format="mp3")
        self._cache[text] = seg
        return seg


def build_countdown(tts: TTS, start: int, pause_ms: int) -> AudioSegment:
    parts: List[AudioSegment] = []
    for i in range(start, 0, -1):
        parts.append(tts.say(str(i)))
        parts.append(silence(pause_ms))
    return sum(parts, AudioSegment.empty())


def schedule_on_timeline(
    total_duration_ms: int,
    events: Iterable[Tuple[int, AudioSegment]],
) -> AudioSegment:
    """
    Construit un segment audio en plaçant chaque event (time_ms, audio) sur une timeline.
    Le temps avance en respectant les timestamps.
    Si un event est en retard (time_ms < current), on le joue immédiatement (sans silence négatif).
    """
    out = AudioSegment.empty()
    current = 0

    for time_ms, seg in sorted(events, key=lambda x: x[0]):
        out += silence(time_ms - current)
        out += seg
        current = len(out)

    # On complète la timeline jusqu'à total_duration_ms (sans couper les events qui dépassent)
    out += silence(total_duration_ms - len(out))
    return out


# -------------------------
# Phases de l'entraînement
# -------------------------

def build_preparation(tts: TTS, duree_s: int) -> AudioSegment:
    # L'annonce puis une vraie préparation silencieuse
    voice = tts.say(f"Vous avez {duree_s} secondes pour vous préparer.")
    return voice + silence(duree_s * 1000)


def build_rest(tts: TTS, duree_s: int) -> AudioSegment:
    voice = tts.say(f"Repos. {duree_s} secondes.")
    return voice + silence(duree_s * 1000)


def build_apnea_phase(tts: TTS, apnee_s: int, cfg: TrainingConfig) -> AudioSegment:
    """
    Planification propre :
    - t=0 : "Apnée !"
    - annonces toutes les 30s (ex: "30 secondes restantes") aux bons moments
    - countdown final à t = apnee - countdown_final
    La timeline dure exactement apnee_s secondes (hors éventuels dépassements de voix).
    """
    apnee_ms = apnee_s * 1000
    events: List[Tuple[int, AudioSegment]] = []

    # Début apnée
    events.append((0, tts.say("Apnée !")))

    # Annonces de temps restant (exclut 0 et exclut les 3 dernières secondes)
    step = cfg.annonce_toutes_les_s
    for remaining in range(apnee_s - step, 0, -step):
        if remaining <= cfg.countdown_final_s:
            continue
        t_ms = (apnee_s - remaining) * 1000
        events.append((t_ms, tts.say(f"{remaining} secondes restantes.")))

    # Countdown final (3..1) placé à apnee-3s
    cd_start_ms = max(0, (apnee_s - cfg.countdown_final_s) * 1000)
    countdown = build_countdown(tts, cfg.countdown_final_s, cfg.pause_entre_chiffres_ms)
    events.append((cd_start_ms, countdown))

    # Construire la timeline de l'apnée
    apnea_timeline = schedule_on_timeline(apnee_ms, events)

    # Fin apnée (signal après la timeline)
    end_cue = tts.say("Respirez.")
    return apnea_timeline + end_cue


def build_training(cfg: TrainingConfig) -> AudioSegment:
    tts = TTS(cfg.langue)
    out = AudioSegment.empty()

    # Intro
    out += tts.say("Préparation.")

    # Préparation
    out += build_preparation(tts, cfg.duree_preparation_s)

    for cycle in range(1, cfg.nombre_cycles + 1):
        apnee_s = cfg.duree_apnee_initiale_s + (cycle - 1) * cfg.augmentation_s

        # Compte à rebours avant apnée
        out += build_countdown(tts, cfg.countdown_avant_apnee_s, cfg.pause_entre_chiffres_ms)

        # Apnée + cues
        out += build_apnea_phase(tts, apnee_s, cfg)

        # Repos (sauf dernier)
        if cycle < cfg.nombre_cycles:
            out += build_rest(tts, cfg.pause_entre_cycles_s)

    return out


# -------------------------
# Run + Export
# -------------------------

cfg = TrainingConfig(
    duree_preparation_s=60,
    duree_apnee_initiale_s=60,
    augmentation_s=15,
    nombre_cycles=8,
    pause_entre_cycles_s=120,
    langue="fr",
)

audio = build_training(cfg)

output_file = "entrainement_apnee_multi_cycles.mp3"
audio.export(output_file, format="mp3")

display(Audio(output_file))

Cellule de code 3 :
ici, cela va permettre d’envoyer le fichier de musique d’info pour qu’il soit utilisable.

Python
from google.colab import files
uploaded = files.upload()  # Puis sélectionner ton fichier MP3

Cellule de code 4 :
on y est presque, c’est le 2e script qui va construire le fichier d’entrainement avec la musique.

Python
from pydub import AudioSegment, effects
from IPython.display import Audio, display

entrainement = audio  # "audio" est le résultat de la première partie

# -------------------------
#mettre ici le chemin du fichier uploadé à l'étape précédente (ouvrir l'onglet Fichiers et clic droit sur le fichier pour récupérer son chemin)
# -------------------------

musique = AudioSegment.from_file("/content/musique-de-fond.mp3")

# 1) Normaliser un peu 
musique = effects.normalize(musique)

# 2) Boucler puis TRONQUER exactement à la durée de l'entraînement
if len(musique) < len(entrainement):
    reps = (len(entrainement) // len(musique)) + 1
    musique = musique * reps
musique = musique[:len(entrainement)]

# 3) Baisser le volume (paramètre à modifier en fonction de la musique de fond)
musique = musique - 14  # (12–20 dB typiquement)

# 4) Fade in/out 
musique = musique.fade_in(2000).fade_out(3000)

# 5) Mixer
mix = musique.overlay(entrainement, position=0)

out = "entrainement_avec_musique.mp3"
mix.export(out, format="mp3", bitrate="192k")
display(Audio(out))

Cellule de code 5 :
juste deux lignes pour pouvoir télécharger le fichier d’entrainement.

Python
from google.colab import files
files.download("entrainement_avec_musique.mp3")

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *