Tutoriel Python : Créer un Modificateur de Voix

📅 28 août 2025
📁 tuto
Tutoriel
Tutoriel Python : Créer un Modificateur de Voix

Introduction : Transformer sa Voix en Temps Réel

Avez-vous déjà voulu modifier votre voix en direct pendant un appel Discord, simuler la voix d'un personnage dans un jeu, ou créer des effets vocaux pour vos vidéos ? C'est exactement ce que nous allons créer dans ce tutoriel !

Nous allons développer un modificateur de voix en temps réel en Python qui permet de :

  • 🎙️ Capturer votre voix depuis le microphone
  • 🎛️ Modifier le pitch (ton) en temps réel
  • 🔊 Diffuser instantanément le son transformé
  • 👨‍👩‍👧 Simuler des voix masculines ou féminines
  • 💾 Enregistrer et sauvegarder vos créations

Ce que vous allez apprendre :

  • Traitement audio en temps réel avec Python
  • Création d'interfaces graphiques avec PyQt5
  • Manipulation du pitch vocal
  • Capture et lecture audio avec sounddevice
  • Enregistrement de fichiers WAV

Niveau requis : Intermédiaire (bases de Python + notions d'audio)

Temps estimé : 1-2 heures

🎯 Présentation du Projet

Ce projet consiste à créer un modificateur de voix en temps réel avec une interface graphique intuitive. L'application capture le flux audio du microphone, applique une transformation de pitch, et diffuse le résultat instantanément.

Architecture du système

┌─────────────────────────────────────────────┐
│     MODIFICATEUR DE VOIX TEMPS RÉEL         │
├─────────────────────────────────────────────┤
│                                             │
│  🎤 MICROPHONE                              │
│       │                                     │
│       ▼                                     │
│  ┌──────────────┐                           │
│  │   Capture    │ ← sounddevice            │
│  │   Audio      │   (44.1 kHz)             │
│  └──────┬───────┘                           │
│         │                                   │
│         ▼                                   │
│  ┌──────────────┐                           │
│  │ Traitement   │ ← numpy                  │
│  │   Pitch      │   (rééchantillonnage)    │
│  └──────┬───────┘                           │
│         │                                   │
│         ├──────────┐                        │
│         │          │                        │
│         ▼          ▼                        │
│  ┌──────────┐  ┌─────────┐                 │
│  │ Lecture  │  │ Enreg.  │                 │
│  │  Audio   │  │  WAV    │                 │
│  └──────────┘  └─────────┘                 │
│       │                                     │
│       ▼                                     │
│  🔊 HAUT-PARLEURS                           │
│                                             │
└─────────────────────────────────────────────┘

✨ Fonctionnalités Principales

1. 🎛️ Interface Graphique Intuitive

L'application dispose d'une interface PyQt5 moderne et facile à utiliser :

  • Slider de pitch - Ajustement fin de -12 à +12 demi-tons
  • Indicateur en temps réel - Affichage du pitch actuel
  • Boutons préréglés - Voix féminine/masculine en un clic
  • Contrôles d'enregistrement - Démarrer/arrêter, prévisualiser, sauvegarder

2. 🎙️ Capture Audio en Temps Réel

Le système capture continuellement l'audio du microphone avec des paramètres optimisés :

  • Fréquence d'échantillonnage : 44.1 kHz (qualité CD)
  • Taille de buffer : 1024 samples (~23 ms de latence)
  • Format : Float32 mono
  • Latence ultra-faible : Pour un feedback instantané

3. 🔊 Modification du Pitch Vocal

Le cœur de l'application : transformation de la hauteur de la voix !

Comment fonctionne le pitch shifting ?

Le pitch (hauteur) d'un son est déterminé par sa fréquence. Pour modifier le pitch :

  1. Rééchantillonnage temporel - On modifie la vitesse de lecture
  2. Conversion pitch ↔ ratio - Formule : ratio = 2^(semitones/12)
  3. Interpolation - Pour maintenir la qualité audio

Exemples de transformation :

Demi-tons Ratio Effet
+12 2.0 Une octave plus aigu
+6 1.41 Voix féminine
0 1.0 Normal
-6 0.71 Voix masculine
-12 0.5 Une octave plus grave

4. 👨‍👩‍👧 Simulation Voix Masculine/Féminine

Boutons préréglés pour transformer rapidement votre voix :

  • Voix féminine → +6 demi-tons (pitch plus aigu)
  • Voix masculine → -6 demi-tons (pitch plus grave)

⚠️ Note : Cette simulation est basique. Pour un résultat plus réaliste, il faudrait également modifier les formants vocaux (voir section améliorations).

5. 📂 Enregistrement et Sauvegarde

L'application permet d'enregistrer vos créations :

  • Enregistrement en mémoire - Capture continue tant que actif
  • Prévisualisation - Écouter avant de sauvegarder
  • Export WAV - Format standard compatible partout

🛠️ Technologies Utilisées

Bibliothèques Python

Bibliothèque Version Rôle
PyQt5 5.15+ Interface graphique (GUI)
sounddevice 0.4+ Capture/lecture audio temps réel
numpy 1.24+ Traitement numérique du signal
soundfile 0.12+ Lecture/écriture fichiers WAV

Concepts audio importants

Fréquence d'échantillonnage (Sample Rate) :

  • 44100 Hz = 44100 échantillons par seconde
  • Standard pour l'audio de qualité CD
  • Permet de capturer des fréquences jusqu'à ~22 kHz (limite audible)

Taille de buffer (Block Size) :

  • 1024 samples = ~23 ms de latence à 44.1 kHz
  • Plus petit = moins de latence mais plus de CPU
  • Plus grand = moins de CPU mais latence perceptible

Format audio (dtype) :

  • float32 = nombres à virgule flottante sur 32 bits
  • Plage : -1.0 à +1.0
  • Meilleur pour le traitement (évite le clipping)

📦 Installation et Configuration

Prérequis

  • Python 3.8 ou supérieur
  • Un microphone fonctionnel
  • Des haut-parleurs ou casque
  • Windows, macOS ou Linux

Étape 1 : Créer l'environnement

# Créer un dossier pour le projet
mkdir voice-modifier && cd voice-modifier

# Créer un environnement virtuel
python -m venv venv

# Activer l'environnement
# Windows:
venv\Scripts\activate
# Linux/Mac:
source venv/bin/activate

Étape 2 : Installer les dépendances

# Installation des bibliothèques
pip install PyQt5 sounddevice numpy soundfile

# Vérifier les installations
python -c "import PyQt5; import sounddevice; import numpy; import soundfile; print('✓ Toutes les dépendances installées!')"

Étape 3 : Vérifier les périphériques audio

Avant de lancer l'application, vérifiez vos périphériques audio disponibles :

# Script de test
import sounddevice as sd

print("=== Périphériques audio disponibles ===")
print(sd.query_devices())

# Tester le microphone
print("\n=== Test d'enregistrement (5 secondes) ===")
duration = 5
print(f"Parlez pendant {duration} secondes...")
recording = sd.rec(int(duration * 44100), samplerate=44100, channels=1)
sd.wait()
print("Lecture...")
sd.play(recording, 44100)
sd.wait()
print("✓ Test terminé!")

💻 Code Source Complet et Commenté

Structure du projet

voice-modifier/
├── voice_modifier.py       # Application principale
├── requirements.txt        # Dépendances
├── README.md              # Documentation
└── recordings/            # Dossier pour les enregistrements

Code principal (voice_modifier.py)

import sys
import numpy as np
import sounddevice as sd
import soundfile as sf
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QSlider,
    QPushButton, QFileDialog
)
from PyQt5.QtCore import Qt, QTimer

# Configuration audio globale
samplerate = 44100  # Fréquence d'échantillonnage (Hz)
blocksize = 1024    # Taille du buffer (samples)

class VoiceModifier(QWidget):
    """Application de modification de voix en temps réel"""
    
    def __init__(self):
        super().__init__()
        
        # Configuration de la fenêtre
        self.setWindowTitle("Modificateur de voix")
        self.setGeometry(300, 300, 400, 350)
        
        # Variables d'état
        self.pitch = 1.0           # Ratio de pitch (1.0 = normal)
        self.recording = []        # Buffer d'enregistrement
        self.is_recording = False  # État d'enregistrement
        
        # Création de l'interface
        self.init_ui()
        
        # Démarrage du flux audio (avec léger délai pour init Qt)
        QTimer.singleShot(500, self.start_audio_stream)
    
    def init_ui(self):
        """Initialise l'interface utilisateur"""
        layout = QVBoxLayout()
        
        # === SECTION 1: CONTRÔLE DU PITCH ===
        
        # Label indicateur de pitch
        self.label = QLabel("Pitch: 0 (Normal)")
        layout.addWidget(self.label)
        
        # Slider de pitch (-12 à +12 demi-tons)
        self.slider = QSlider(Qt.Horizontal)
        self.slider.setMinimum(-12)
        self.slider.setMaximum(12)
        self.slider.setValue(0)
        self.slider.valueChanged.connect(self.change_pitch)
        layout.addWidget(self.slider)
        
        # === SECTION 2: BOUTONS PRÉRÉGLÉS ===
        
        # Bouton voix féminine (+6 demi-tons)
        self.female_button = QPushButton("Voix féminine")
        self.female_button.clicked.connect(lambda: self.slider.setValue(6))
        layout.addWidget(self.female_button)
        
        # Bouton voix masculine (-6 demi-tons)
        self.male_button = QPushButton("Voix masculine")
        self.male_button.clicked.connect(lambda: self.slider.setValue(-6))
        layout.addWidget(self.male_button)
        
        # === SECTION 3: ENREGISTREMENT ===
        
        # Bouton démarrer/arrêter enregistrement
        self.record_button = QPushButton("Démarrer l'enregistrement")
        self.record_button.clicked.connect(self.toggle_recording)
        layout.addWidget(self.record_button)
        
        # Bouton prévisualisation
        self.preview_button = QPushButton("Préécouter l'enregistrement")
        self.preview_button.clicked.connect(self.preview_recording)
        layout.addWidget(self.preview_button)
        
        # Bouton sauvegarde WAV
        self.save_button = QPushButton("Enregistrer dans un fichier WAV")
        self.save_button.clicked.connect(self.save_recording)
        layout.addWidget(self.save_button)
        
        self.setLayout(layout)
    
    def change_pitch(self, value):
        """
        Modifie le pitch vocal
        
        Formule: pitch_ratio = 2^(semitones/12)
        
        Exemples:
        - +12 demi-tons → 2^(12/12) = 2.0 (octave supérieure)
        - +6 demi-tons → 2^(6/12) = 1.41 (voix plus aiguë)
        - -6 demi-tons → 2^(-6/12) = 0.71 (voix plus grave)
        """
        self.pitch = 2 ** (value / 12.0)
        
        # Mise à jour du label
        sign = '+' if value > 0 else ''
        self.label.setText(f"Pitch: {value} ({sign}{value} demi-tons)")
    
    def toggle_recording(self):
        """Active/désactive l'enregistrement"""
        self.is_recording = not self.is_recording
        
        if self.is_recording:
            # Démarrage: vider le buffer et changer le texte du bouton
            self.recording.clear()
            self.record_button.setText("Arrêter l'enregistrement")
            print("🔴 Enregistrement démarré...")
        else:
            # Arrêt: changer le texte du bouton
            self.record_button.setText("Démarrer l'enregistrement")
            print(f"⏹️ Enregistrement arrêté ({len(self.recording)} blocs)")
    
    def preview_recording(self):
        """Prévisualise l'enregistrement actuel"""
        if not self.recording:
            print("⚠️ Aucun enregistrement à lire.")
            return
        
        # Concaténer tous les blocs audio
        audio_data = np.concatenate(self.recording)
        
        # Lecture
        sd.play(audio_data, samplerate)
        print(f"▶️ Préécoute en cours... ({len(audio_data)/samplerate:.1f}s)")
    
    def save_recording(self):
        """Sauvegarde l'enregistrement dans un fichier WAV"""
        if not self.recording:
            print("⚠️ Aucun enregistrement disponible.")
            return
        
        # Dialogue de sauvegarde
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "Enregistrer sous", 
            "", 
            "Fichiers WAV (*.wav)"
        )
        
        if file_path:
            # Concaténer et sauvegarder
            audio_data = np.concatenate(self.recording)
            sf.write(file_path, audio_data, samplerate)
            
            duration = len(audio_data) / samplerate
            print(f"💾 Enregistrement sauvegardé: {file_path} ({duration:.1f}s)")
    
    def start_audio_stream(self):
        """Démarre le flux audio en temps réel"""
        print("🎙️ Démarrage du flux audio...")
        
        self.stream = sd.InputStream(
            samplerate=samplerate,      # 44.1 kHz
            blocksize=blocksize,        # 1024 samples
            channels=1,                 # Mono
            dtype='float32',            # Format float
            callback=self.audio_callback  # Fonction de traitement
        )
        
        self.stream.start()
        print("✓ Flux audio actif!")
    
    def audio_callback(self, indata, frames, time, status):
        """
        Callback appelé pour chaque bloc audio
        
        Cette fonction est appelée en continu par sounddevice
        pour traiter l'audio en temps réel.
        
        Args:
            indata: Données audio entrantes (numpy array)
            frames: Nombre de frames
            time: Informations temporelles
            status: Statut du stream
        """
        # Afficher les erreurs éventuelles
        if status:
            print("⚠️ Status:", status)
        
        # Extraire le canal mono
        data = indata[:, 0]
        
        # === ALGORITHME DE PITCH SHIFTING ===
        
        # Créer les indices de rééchantillonnage
        # Si pitch > 1 → indices plus espacés → son plus aigu
        # Si pitch < 1 → indices plus rapprochés → son plus grave
        indices = np.arange(0, len(data), self.pitch)
        
        # Limiter les indices à la longueur des données
        indices = indices[indices < len(data)].astype(int)
        
        # Rééchantillonner
        shifted = data[indices]
        
        # Adapter à la longueur originale du buffer
        if len(shifted) < len(data):
            # Padding avec des zéros si trop court
            shifted = np.pad(
                shifted, 
                (0, len(data) - len(shifted)), 
                mode='constant'
            )
        else:
            # Tronquer si trop long
            shifted = shifted[:len(data)]
        
        # === ENREGISTREMENT (si actif) ===
        if self.is_recording:
            # Copier dans le buffer d'enregistrement
            self.recording.append(np.copy(shifted))
        
        # Note: Dans cette version, l'audio modifié n'est PAS
        # renvoyé vers les haut-parleurs automatiquement.
        # Pour ça, il faudrait utiliser sd.OutputStream ou
        # un câble audio virtuel (VB-Cable).

if __name__ == "__main__":
    # Créer l'application Qt
    app = QApplication(sys.argv)
    
    # Créer et afficher la fenêtre
    window = VoiceModifier()
    window.show()
    
    # Lancer la boucle d'événements
    sys.exit(app.exec_())

Fichier requirements.txt

PyQt5>=5.15.0
sounddevice>=0.4.6
numpy>=1.24.0
soundfile>=0.12.0

🚀 Utilisation de l'Application

Lancement

# Activer l'environnement virtuel
source venv/bin/activate  # Linux/Mac
# ou
venv\Scripts\activate  # Windows

# Lancer l'application
python voice_modifier.py

Guide d'utilisation pas-à-pas

1. Ajuster le pitch manuellement

  1. Déplacer le slider horizontalement
  2. Vers la droite = voix plus aiguë (+)
  3. Vers la gauche = voix plus grave (-)
  4. L'indicateur affiche le nombre de demi-tons

2. Utiliser les préréglages

  • Cliquer sur "Voix féminine" pour un pitch +6
  • Cliquer sur "Voix masculine" pour un pitch -6
  • Parler dans le micro et écouter le résultat

3. Enregistrer votre voix modifiée

  1. Ajuster le pitch désiré
  2. Cliquer "Démarrer l'enregistrement"
  3. Parler dans le microphone
  4. Cliquer "Arrêter l'enregistrement"
  5. Optionnel: "Préécouter" pour vérifier
  6. Cliquer "Enregistrer dans un fichier WAV"
  7. Choisir l'emplacement et le nom

🔊 Diffuser l'Audio Modifié en Temps Réel

⚠️ Note importante : Le code actuel enregistre l'audio modifié mais ne le diffuse PAS automatiquement dans les haut-parleurs. Pour cela, il faut :

Solution 1: Ajouter un flux de sortie

Modifier le code pour inclure un OutputStream :

def start_audio_stream(self):
    """Démarre le flux audio avec sortie"""
    
    # Stream d'entrée ET de sortie combiné
    self.stream = sd.Stream(
        samplerate=samplerate,
        blocksize=blocksize,
        channels=1,
        dtype='float32',
        callback=self.audio_callback_io
    )
    
    self.stream.start()

def audio_callback_io(self, indata, outdata, frames, time, status):
    """Callback avec entrée ET sortie"""
    if status:
        print("Status:", status)
    
    # Traitement
    data = indata[:, 0]
    indices = np.arange(0, len(data), self.pitch)
    indices = indices[indices < len(data)].astype(int)
    shifted = data[indices]
    
    # Adapter la longueur
    if len(shifted) < len(data):
        shifted = np.pad(shifted, (0, len(data) - len(shifted)), mode='constant')
    else:
        shifted = shifted[:len(data)]
    
    # ENVOYER vers la sortie
    outdata[:, 0] = shifted
    
    # Enregistrement si actif
    if self.is_recording:
        self.recording.append(np.copy(shifted))

Solution 2: Utiliser un câble audio virtuel

VB-Cable (Windows/Mac) :

  1. Télécharger VB-Cable: vb-audio.com/Cable
  2. Installer le pilote audio virtuel
  3. Dans l'app, sélectionner "CABLE Input" comme sortie
  4. Dans Discord/Skype, sélectionner "CABLE Output" comme micro

Pour Linux (PulseAudio) :

# Créer un module loopback
pactl load-module module-loopback latency_msec=1

# Ou utiliser pavucontrol pour router manuellement
sudo apt install pavucontrol
pavucontrol

💡 Cas d'Usage Pratiques

1. Discord / Gaming

Scénario : Changer sa voix en direct pendant les parties

  1. Installer VB-Cable
  2. Lancer le modificateur de voix
  3. Configurer Discord:
    • Paramètres → Voix et Vidéo
    • Périphérique d'entrée → CABLE Output
  4. Ajuster le pitch selon vos envies
  5. Vos amis entendront votre voix modifiée!

2. Création de Contenu (YouTube, TikTok)

Scénario : Créer des personnages avec voix distinctes

  1. Ajuster pitch pour chaque personnage
  2. Enregistrer les dialogues séparément
  3. Sauvegarder chaque fichier WAV
  4. Importer dans votre éditeur vidéo

Exemples de personnages :

  • Robot → pitch -10, voix monotone
  • Enfant → pitch +8, parler vite
  • Monstre → pitch -12, voix grave
  • Elfe → pitch +5, voix douce

3. Podcasts et Doublages

Scénario : Diversifier les voix dans vos créations audio

  • Créer plusieurs personnages pour un dialogue
  • Protéger son identité vocale
  • Corriger le pitch pour améliorer la clarté

4. Appels Anonymes

Scénario : Masquer votre voix réelle

⚠️ Légal uniquement : Assurez-vous que l'utilisation est légale dans votre juridiction. Ne pas utiliser pour harcèlement ou fraude.

🔧 Améliorations et Optimisations

1. Améliorer la Qualité du Pitch Shifting

Problème actuel : L'algorithme de rééchantillonnage simple peut créer des artéfacts (sons robotiques, distorsion).

Solution : Utiliser un algorithme plus avancé comme PSOLA ou phase vocoder.

# Installer librosa pour meilleur pitch shifting
pip install librosa

import librosa

def pitch_shift_advanced(audio, semitones):
    """Pitch shifting de qualité supérieure avec librosa"""
    return librosa.effects.pitch_shift(
        audio,
        sr=samplerate,
        n_steps=semitones,
        bins_per_octave=12
    )

2. Ajouter des Effets Audio

Réverbération :

from scipy import signal

def add_reverb(audio, room_size=0.5):
    """Ajoute de la réverbération"""
    # Créer une réponse impulsionnelle
    ir_length = int(samplerate * room_size)
    ir = np.random.randn(ir_length) * np.exp(-np.arange(ir_length) / (ir_length / 3))
    
    # Convolution
    return signal.convolve(audio, ir, mode='same')

Écho :

def add_echo(audio, delay_ms=500, decay=0.5):
    """Ajoute un écho"""
    delay_samples = int(samplerate * delay_ms / 1000)
    echo = np.zeros(len(audio) + delay_samples)
    
    echo[:len(audio)] = audio
    echo[delay_samples:] += audio * decay
    
    return echo[:len(audio)]

Distorsion (effet robot) :

def robotize(audio, amount=0.5):
    """Effet voix robotique"""
    # Quantification du signal
    levels = int(32 * (1 - amount))
    quantized = np.round(audio * levels) / levels
    return quantized

3. Modification des Formants

Pour une simulation homme/femme plus réaliste, il faut modifier les formants vocaux (résonances du tract vocal) en plus du pitch.

# Utiliser praat-parselmouth pour formants
pip install praat-parselmouth

import parselmouth

def change_gender(audio, formant_shift=1.2):
    """Change les formants pour simuler changement de genre"""
    sound = parselmouth.Sound(audio, sampling_frequency=samplerate)
    
    manipulation = parselmouth.praat.call(sound, "To Manipulation", 0.01, 75, 600)
    
    # Modifier les formants
    parselmouth.praat.call(
        manipulation, 
        "Change gender", 
        75, 600, formant_shift, 0, 1.0, 1.0
    )
    
    return parselmouth.praat.call(manipulation, "Get resynthesis (overlap-add)")

4. Interface Améliorée

Ajouts suggérés :

  • ✅ Visualisation du waveform en temps réel
  • ✅ Spectrogramme (affichage fréquentiel)
  • ✅ VU-Meter pour niveau audio
  • ✅ Préréglages personnalisables et sauvegardables
  • ✅ Sélection du périphérique d'entrée/sortie
  • ✅ Égaliseur graphique
from PyQt5.QtWidgets import QComboBox

# Ajouter sélection de périphérique
self.device_combo = QComboBox()
devices = sd.query_devices()
for i, device in enumerate(devices):
    if device['max_input_channels'] > 0:
        self.device_combo.addItem(f"{i}: {device['name']}")
layout.addWidget(self.device_combo)

5. Clonage Vocal avec IA (Avancé)

Pour aller plus loin, intégrer un modèle de clonage vocal comme RVC (Retrieval-based Voice Conversion).

RVC permet de :

  • Cloner n'importe quelle voix à partir d'échantillons
  • Transformer votre voix en celle de quelqu'un d'autre
  • Qualité quasi-parfaite avec modèles entraînés

Installation RVC :

# Cloner le repo RVC
git clone https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI.git
cd Retrieval-based-Voice-Conversion-WebUI

# Installer les dépendances
pip install -r requirements.txt

# Télécharger les modèles pré-entraînés
# (voir documentation RVC)

Intégration dans votre app :

import torch
from rvc_infer import RVCInference

class VoiceModifierAI(VoiceModifier):
    def __init__(self):
        super().__init__()
        
        # Charger modèle RVC
        self.rvc_model = RVCInference(
            model_path="models/my_voice.pth",
            config_path="configs/config.json"
        )
    
    def audio_callback_ai(self, indata, outdata, frames, time, status):
        """Callback avec transformation IA"""
        data = indata[:, 0]
        
        # Conversion vocale avec RVC
        converted = self.rvc_model.convert(
            data,
            pitch_shift=self.pitch
        )
        
        outdata[:, 0] = converted

🐛 Dépannage et Problèmes Courants

Problème 1: Pas de son capturé

Symptômes : Aucun son n'est enregistré ou traité

Solutions :

# Vérifier les périphériques
import sounddevice as sd
print(sd.query_devices())

# Définir le périphérique par défaut manuellement
sd.default.device = 1  # Remplacer par l'ID de votre micro

# Tester la capture
recording = sd.rec(int(2 * samplerate), samplerate=samplerate, channels=1)
sd.wait()
print(f"Volume max enregistré: {np.max(np.abs(recording))}")
# Si ~0.0 → micro muet ou mauvais périphérique

Problème 2: Latence trop élevée

Symptômes : Délai perceptible entre parole et retour audio

Solutions :

  • Réduire blocksize (essayer 512 ou 256)
  • Utiliser ASIO sur Windows (pilotes faible latence)
  • Fermer autres applications audio
# Configuration faible latence
blocksize = 256  # ~6ms de latence

# Sur Windows, utiliser ASIO
sd.default.device = 'ASIO'  # Si installé

Problème 3: Son distordu ou haché

Causes possibles :

  • CPU surchargé
  • Buffer trop petit
  • Pitch shifting trop extrême

Solutions :

# Augmenter blocksize
blocksize = 2048

# Limiter le pitch
if abs(self.pitch - 1.0) > 2.0:  # Limiter à +/-1 octave
    self.pitch = np.clip(self.pitch, 0.5, 2.0)

# Utiliser algorithme plus performant (voir améliorations)

Problème 4: PyQt5 ne s'installe pas

Erreur : ERROR: Could not build wheels for PyQt5

Solutions :

# Sur Linux
sudo apt install python3-pyqt5 python3-pyqt5.qtmultimedia

# Sur Mac avec Homebrew
brew install pyqt5

# Alternative: utiliser PyQt6
pip uninstall PyQt5
pip install PyQt6
# Puis remplacer "PyQt5" par "PyQt6" dans le code

Problème 5: "PortAudio error"

Solution Linux :

sudo apt install portaudio19-dev python3-pyaudio
pip install --upgrade sounddevice

Solution Mac :

brew install portaudio
pip install --upgrade sounddevice

Solution Windows :

  • Réinstaller sounddevice
  • Vérifier que le micro n'est pas utilisé par autre app
  • Autoriser l'accès micro dans Paramètres Windows

📚 Ressources et Apprentissage

Documentation officielle

Tutoriels et cours

  • Digital Signal Processing - Cours gratuits sur Coursera
  • Audio Programming - The Audio Programmer (YouTube)
  • Music Information Retrieval - Cours Stanford

Communautés

  • Reddit: r/audioengineering, r/DSP
  • Discord: Audio Developers
  • Stack Overflow: Tags python-sounddevice, audio-processing

🎓 Concepts Théoriques

Comprendre le Pitch Shifting

Méthode 1: Time-Domain (utilisée dans ce projet)

  • Rééchantillonnage temporel
  • Simple mais peut créer des artéfacts
  • Rapide en calcul

Méthode 2: Frequency-Domain

  • Transformée de Fourier (FFT)
  • Modification des fréquences
  • Transformée inverse (IFFT)
  • Meilleure qualité mais plus lourd en CPU

Méthode 3: PSOLA (Pitch Synchronous Overlap-Add)

  • Standard professionnel
  • Détection de pitch
  • Manipulation de marqueurs temporels
  • Excellente qualité

La Formule Magique

pitch_ratio = 2^(semitones / 12)

Pourquoi cette formule ?
- Une octave = doubler la fréquence = ratio 2.0
- Une octave = 12 demi-tons
- Donc: 2^(12/12) = 2^1 = 2 ✓

Exemples:
- +1 demi-ton  → 2^(1/12)  = 1.059 (5.9% plus rapide)
- +12 demi-tons → 2^(12/12) = 2.000 (2x plus rapide = octave)
- -12 demi-tons → 2^(-12/12) = 0.500 (2x plus lent = octave grave)

🚀 Déploiement et Distribution

Créer un exécutable standalone

Avec PyInstaller :

# Installer PyInstaller
pip install pyinstaller

# Créer l'exécutable
pyinstaller --onefile --windowed --name="VoiceModifier" voice_modifier.py

# L'exécutable sera dans dist/
# Windows: VoiceModifier.exe
# Mac: VoiceModifier.app
# Linux: VoiceModifier

Options avancées :

# Ajouter une icône
pyinstaller --onefile --windowed --icon=icon.ico --name="VoiceModifier" voice_modifier.py

# Inclure des fichiers supplémentaires
pyinstaller --onefile --windowed --add-data "models:models" voice_modifier.py

Publier sur GitHub

# Créer .gitignore
echo "venv/
__pycache__/
*.pyc
*.wav
dist/
build/
*.spec" > .gitignore

# Initialiser git
git init
git add .
git commit -m "Initial commit: Voice Modifier v1.0"

# Créer repo sur GitHub et pousser
git remote add origin https://github.com/username/voice-modifier.git
git push -u origin main

📊 Performances et Optimisations

Benchmarks

Configuration Latence CPU Usage
blocksize=256 ~6 ms ~15%
blocksize=512 ~12 ms ~10%
blocksize=1024 ~23 ms ~5%
blocksize=2048 ~46 ms ~3%

Recommandations :

  • Pour streaming live → blocksize=512 (bon compromis)
  • Pour enregistrement → blocksize=2048 (moins de CPU)
  • Pour gaming → blocksize=256 (latence minimale)

📝 Conclusion

Félicitations ! Vous venez de créer un modificateur de voix en temps réel fonctionnel et extensible en Python. Ce projet combine traitement audio numérique, interfaces graphiques et programmation temps réel.

Ce que vous avez appris :

  • ✅ Capture et lecture audio avec sounddevice
  • ✅ Traitement numérique du signal avec NumPy
  • ✅ Création d'interfaces PyQt5
  • ✅ Algorithmes de pitch shifting
  • ✅ Gestion de buffers audio temps réel
  • ✅ Enregistrement et export WAV

Prochaines étapes suggérées :

  1. Implémenter la diffusion audio en temps réel
  2. Ajouter des effets audio (reverb, echo, chorus)
  3. Améliorer l'algorithme de pitch shifting (librosa, PSOLA)
  4. Créer des préréglages de voix (robot, alien, etc.)
  5. Intégrer le clonage vocal avec RVC
  6. Publier votre projet sur GitHub

Le traitement audio est un domaine fascinant avec d'innombrables possibilités. N'hésitez pas à expérimenter, à améliorer le code, et à partager vos créations !

⚠️ Note éthique : Utilisez cet outil de manière responsable. Le clonage ou l'imitation de voix sans consentement peut être illégal et nuire à autrui. Respectez toujours la vie privée et les droits des autres.


Article rédigé par Matteo - Développeur passionné de traitement audio et d'applications Python. Retrouvez plus de tutoriels sur mat-univer.tech.

Dernière mise à jour : Février 2026

💬 Questions ou suggestions? Commentez ci-dessous! Je réponds à toutes les questions techniques et j'adore voir vos améliorations!

🎤 Partagez vos créations! Si vous créez des effets vocaux intéressants ou ajoutez des fonctionnalités, partagez-les dans les commentaires!