Skip to content

Commit 3319b8b

Browse files
committed
post(voice-meeting): Add new post on how to generate voice meetings
1 parent 8e2c787 commit 3319b8b

File tree

4 files changed

+228
-1
lines changed

4 files changed

+228
-1
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
source 'https://rubygems.org'
22

3-
gem 'jekyll', '4.4.0'
3+
gem 'jekyll', '4.4.1'
44

55
group :jekyll_plugins do
66
gem 'nokogiri', '= 1.18.2'
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
layout: post
3+
title: "Comment générer des conversations vocales grâce à l'IA pour tester un outil de transcription"
4+
date: 2025-04-11 13:47:00 +0100
5+
description: "Un retour d'expérience sur la génération de dialogues audio synthétiques avec OuteTTS pour tester un outil de transcription de réunion."
6+
img: generate-voice-conversations-ai.jpg
7+
fig-caption: Photo générée avec Le Chat de Mistral AI
8+
tags: ["TTS", "synthetic-voices", "AI", "OuteTTS"]
9+
lang: fr
10+
permalink: /generate-voice-conversations-ai/
11+
status: finished
12+
---
13+
14+
Développant seul un outil de transcription et d’analyse de réunions, j’ai rapidement eu besoin de fichiers audio réalistes pour le tester. N’ayant ni participants pour enregistrer des réunions fictives, ni enregistrements exploitables sous la main, j’ai décidé de créer un générateur de conversations vocales à partir de fichiers texte.
15+
16+
L’idée : partir d’un script de conversation structuré (avec plusieurs intervenants), et utiliser l’IA pour produire automatiquement un fichier audio où chaque personnage s’exprime avec une voix synthétique différente.
17+
18+
Pour cela, je me suis basé sur les modèles TTS existants de [HuggingFace](https://huggingface.co/models?pipeline_tag=text-to-speech&sort=trending){:target="_blank" rel="noopener noreferrer nofollow"} et j'ai sélectionné [**OuteTTS**](https://github.com/edwko/OuteTTS){:target="_blank" rel="noopener noreferrer nofollow"}, un projet open-source de synthèse vocale basé sur **llama.cpp** et la librairie **Transformers**, que du beau monde, et aussi capable de générer des voix naturelles dans différentes langues.
19+
20+
Dans cet article, je partage le fonctionnement du générateur que j’ai construit avec Python, comment j’ai géré les profils de locuteurs, les erreurs rencontrées, et les astuces pour obtenir un résultat fluide, personnalisable, et utile pour tester tout outil de transcription.
21+
22+
{% github_card jeanjerome/VoiceGenMeeting %}
23+
24+
25+
<hr class="hr-text" data-content="Sommaire">
26+
27+
* TOC
28+
{:toc}
29+
30+
<hr class="hr-text" data-content="Générateur">
31+
32+
## Le générateur : principe et structure
33+
34+
L’idée du générateur est simple : partir d’un fichier texte dans lequel chaque ligne correspond à une réplique, précédée du nom de l’intervenant, par exemple :
35+
36+
{% highlight plaintext %}
37+
Marc : Bonjour à tous, on commence ?
38+
Julie : Oui, je suis prête.
39+
...
40+
{% endhighlight %}
41+
42+
Le script identifie automatiquement les locuteurs, leur assigne une voix, puis génère chaque segment audio grâce au modèle OuteTTS. Tous les segments sont ensuite concaténés, avec un petit silence entre chaque prise de parole, pour produire un seul fichier `.wav` qui restitue toute la conversation.
43+
44+
J’ai choisi de construire un outil en ligne de commande, en Python, qui prend un fichier texte en entrée et génère un fichier audio en sortie. Le fonctionnement est assez modulaire et se découpe en plusieurs étapes :
45+
46+
1. **Lecture et parsing du fichier texte** : chaque ligne est analysée pour en extraire le nom du locuteur et son texte.
47+
2. **Attribution d’une voix** : pour chaque nouvel interlocuteur, le script crée un profil vocal unique. C’est une fonctionnalité offerte par OuteTTS, qui permet de générer un clone de voix à partir d’un simple fichier audio d’environ 15 secondes.
48+
3. **Génération des segments audio** : le modèle OuteTTS est invoqué pour chaque réplique, en utilisant la voix associée au locuteur concerné.
49+
4. **Concaténation des segments** : les segments audio sont ensuite combinés dans l’ordre d’apparition, avec une courte pause entre chaque prise de parole pour rendre l’écoute plus naturelle.
50+
51+
Ce fonctionnement permet de simuler très facilement des réunions avec plusieurs intervenants, chacun doté d’une voix propre, à partir d’un simple fichier `.txt`. C’est un excellent moyen de produire des cas de test réalistes sans devoir passer par l’enregistrement audio manuel.
52+
53+
<hr class="hr-text" data-content="Générateur">
54+
55+
## Des voix sur mesure pour chaque interlocuteur
56+
57+
Pour que les conversations générées soient crédibles, il était essentiel que chaque intervenant ait sa propre voix. OuteTTS permet précisément cela : il est capable de créer un profil vocal personnalisé à partir d’un simple fichier audio.
58+
59+
Le script que j’ai développé suit une logique simple : lorsqu’un nouveau nom de locuteur est rencontré, il cherche un fichier audio correspondant dans un dossier `data/speakers`. Par exemple, si la ligne du fichier de transcription commence par `Julie : ...`, le script tentera de charger `data/speakers/julie.wav` (ou `.mp3`).
60+
61+
Si ce fichier existe, OuteTTS l’utilise pour générer un clone de voix en quelques secondes. En réalité, cette opération s’appuie sur `Whisper`, le modèle de transcription audio d’OpenAI, qui est utilisé en amont pour transcrire le fichier et segmenter précisément la voix. La durée utile du fichier est automatiquement tronquée à `15 secondes`, ce qui est suffisant pour capturer les caractéristiques vocales nécessaires.
62+
63+
Le profil généré est ensuite sauvegardé dans un fichier `.json` dans un dossier `data/profiles`, afin de ne pas avoir à le régénérer à chaque exécution. Si aucun fichier n’est trouvé, le script peut utiliser la voix par défaut proposée par le modèle, `en-female-1-neutral` (mais ce n'est pas le but).
64+
65+
Pour simplifier la préparation des voix, le script convertit automatiquement les fichiers `.mp3` (ou `.wav`) en mono, 44,1 kHz, et coupe à 15 secondes maximum si nécessaire. Cela garantit la compatibilité avec les exigences du modèle, tout en permettant d’utiliser des extraits vocaux simples, même enregistrés rapidement avec un micro de base.
66+
67+
Ce système rend la création de dialogues vocaux entièrement automatisable. Il suffit de préparer un script texte avec des noms de personnages, d’ajouter un court fichier vocal pour chacun, et de lancer le générateur pour obtenir une réunion fictive crédible en quelques minutes.
68+
69+
<hr class="hr-text" data-content="Les Pièges">
70+
71+
## Les pièges rencontrés et les ajustements nécessaires
72+
73+
Mettre en place un générateur de réunions audio semblait simple sur le papier… mais plusieurs détails techniques m’ont rapidement forcé à ajuster le tir. Voici quelques obstacles que j’ai rencontrés, et comment je les ai contournés.
74+
75+
### 1. Une seule voix disponible par défaut
76+
En testant le modèle OuteTTS initial, je me suis vite rendu compte que le modèle ne propose qu’un seul profil vocal par défaut (`en-female-1-neutral`). Toute tentative de charger une autre voix renvoyait une erreur.
77+
78+
**Solution** : utiliser des fichiers audio personnalisés pour chaque intervenant. Cela m’a permis de générer dynamiquement autant de profils que nécessaire à partir de mes propres extraits (`marc.mp3`, `julie.wav`, etc.).
79+
80+
81+
### 2. Format audio : Tensor ou WAV ?
82+
Contrairement à ce que l’on pourrait attendre, la sortie `output.audio` retournée par OuteTTS **n’est pas toujours un fichier WAV**. Il s’agit en réalité d’un **tenseur brut** (typiquement un `numpy.ndarray` ou un `torch.Tensor`) représentant le signal audio.
83+
84+
**Solution** : au lieu de traiter le flux comme un fichier encodé (ce qui générait une erreur du type “a bytes-like object is required”), j’ai simplement converti le tenseur en tableau numpy via `np.asarray(output.audio, dtype='float32').flatten()`. Cette approche a résolu le problème et permis la concaténation correcte des segments.
85+
86+
### 3. Voix ralentie, grave, ou déformée
87+
Lors de mes premiers tests, la voix générée était étrange : lente, grave, presque déformée. En réalité, cela venait d’un **mauvais taux d’échantillonnage** lors de l’enregistrement du fichier final.
88+
89+
**Solution** : fixer manuellement le `sample_rate` à **44 100 Hz**, ce qui correspond au taux de sortie audio attendu pour obtenir une voix normale et naturelle avec OuteTTS.
90+
91+
### 4. Incompatibilité avec NumPy 2.0
92+
Une mise à jour de NumPy a introduit un `DeprecationWarning` lié à la gestion du paramètre `copy` lors de la conversion en array. Ce n’était pas bloquant, mais cela polluait la sortie console.
93+
94+
**Solution** : éviter `np.array(..., copy=True)` et préférer `np.asarray(...).copy()` pour garantir la compatibilité avec NumPy 2.0 tout en restant clair.
95+
96+
### 5. Découper automatiquement les fichiers audio trop longs
97+
Certaines de mes sources audio dépassaient 30 secondes, ce qui posait problème à OuteTTS pour générer un profil de voix fiable. Le modèle coupe automatiquement à 15 secondes, mais mieux vaut le faire en amont.
98+
99+
**Solution** : j’ai intégré la librairie `pydub` pour convertir et tronquer automatiquement tout fichier `.mp3` ou `.wav` à 15 secondes, en mono et à 44,1 kHz. Cette étape garantit que les profils sont compatibles, même à partir de fichiers audio imparfaits.
100+
101+
Ces petits ajustements m’ont permis de stabiliser l’outil et d’obtenir une génération fluide, fiable, et surtout réutilisable dans différents contextes de test. Et comme souvent avec l’IA générative, le diable est dans les détails.
102+
103+
104+
<hr class="hr-text" data-content="Conclusion">
105+
106+
## Conclusion
107+
108+
En combinant un simple fichier texte, quelques extraits audio, et un modèle open source comme OuteTTS, j’ai pu créer un outil capable de générer automatiquement des conversations vocales réalistes pour tester mon pipeline de transcription et d’analyse de réunions.
109+
110+
Ce générateur me permet de simuler des échanges multi-intervenants, de varier les voix, et de produire autant de cas de test que nécessaire, sans dépendre d’enregistrements réels ni mobiliser d'autres personnes.
111+
112+
Au-delà du test, cet outil ouvre la voie à d’autres usages : restitution vocale de comptes rendus, relecture audio synthétique de réunions, ou même génération de dialogues simulés à la volée dans des environnements d’apprentissage ou de prototypage d’agents conversationnels.
113+
114+
Le code complet est en open source. Si ce genre d’approche vous intéresse, ou si vous développez des outils de transcription, de TTS ou d’assistants vocaux, je serais ravi d’échanger avec vous.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
layout: post
3+
title: "How to Generate Voice Conversations with AI to Test a Transcription Tool"
4+
date: 2025-04-11 13:47:00 +0100
5+
description: "A feedback on generating synthetic audio dialogues using OuteTTS to test a meeting transcription tool."
6+
img: generate-voice-conversations-ai.jpg
7+
fig-caption: Photo generated with Le Chat by Mistral AI
8+
tags: ["TTS", "synthetic-voices", "AI", "OuteTTS"]
9+
lang: en
10+
permalink: /generate-voice-conversations-ai/
11+
status: finished
12+
---
13+
14+
Developing a meeting transcription and analysis tool on my own, I quickly needed realistic audio files to test it. Without any participants to record mock meetings, nor any usable recordings at hand, I decided to create a voice conversation generator from text files.
15+
16+
The idea: start from a structured conversation script (with multiple speakers), and use AI to automatically produce an audio file where each character speaks with a different synthetic voice.
17+
18+
To do this, I relied on existing TTS models from [HuggingFace](https://huggingface.co/models?pipeline_tag=text-to-speech&sort=trending){:target="_blank" rel="noopener noreferrer nofollow"} and selected [**OuteTTS**](https://github.com/edwko/OuteTTS){:target="_blank" rel="noopener noreferrer nofollow"}, an open-source voice synthesis project based on **llama.cpp** and the **Transformers** library—top-notch tools—and also capable of generating natural voices in different languages.
19+
20+
In this article, I share how the generator I built with Python works, how I managed speaker profiles, the issues I encountered, and some tips for getting a smooth, customizable, and practical result to test any transcription tool.
21+
22+
{% github_card jeanjerome/VoiceGenMeeting %}
23+
24+
25+
<hr class="hr-text" data-content="Table of Contents">
26+
27+
* TOC
28+
{:toc}
29+
30+
<hr class="hr-text" data-content="Generator">
31+
32+
## The Generator: Concept and Structure
33+
34+
The idea behind the generator is simple: start from a text file where each line corresponds to a dialogue, preceded by the speaker's name, for example:
35+
36+
{% highlight plaintext %}
37+
Marc: Hello everyone, shall we start?
38+
Julie: Yes, I’m ready.
39+
...
40+
{% endhighlight %}
41+
42+
The script automatically identifies the speakers, assigns them a voice, then generates each audio segment using the OuteTTS model. All segments are then concatenated, with a short silence between each line, to produce a single `.wav` file that plays the entire conversation.
43+
44+
I chose to build a command-line tool in Python, which takes a text file as input and outputs an audio file. The process is quite modular and consists of several steps:
45+
46+
1. **Reading and parsing the text file**: each line is analyzed to extract the speaker's name and their dialogue.
47+
2. **Voice assignment**: for each new speaker, the script creates a unique voice profile. This is a feature provided by OuteTTS, which can generate a voice clone from a simple 15-second audio file.
48+
3. **Generating audio segments**: the OuteTTS model is invoked for each line, using the voice assigned to the respective speaker.
49+
4. **Concatenating the segments**: the audio segments are then combined in the order they appear, with a short pause between each to make the listening experience more natural.
50+
51+
This setup allows for easily simulating meetings with multiple speakers, each with their own voice, from a simple `.txt` file. It’s an excellent way to create realistic test cases without needing manual audio recording.
52+
53+
<hr class="hr-text" data-content="Generator">
54+
55+
## Custom Voices for Each Speaker
56+
57+
To make the generated conversations believable, it was essential for each participant to have their own voice. OuteTTS enables precisely this: it can create a personalized voice profile from a simple audio file.
58+
59+
The script I developed follows a straightforward logic: when a new speaker name is encountered, it looks for a corresponding audio file in the `data/speakers` folder. For example, if a line in the transcript file begins with `Julie: ...`, the script will attempt to load `data/speakers/julie.wav` (or `.mp3`).
60+
61+
If the file exists, OuteTTS uses it to generate a voice clone in just a few seconds. In fact, this process relies on `Whisper`, OpenAI’s audio transcription model, which is used upstream to transcribe the file and accurately segment the voice. The usable length of the file is automatically trimmed to `15 seconds`, which is sufficient to capture the necessary vocal characteristics.
62+
63+
The generated profile is then saved as a `.json` file in the `data/profiles` folder, so it does not need to be regenerated on every run. If no file is found, the script can fall back on the model’s default voice, `en-female-1-neutral` (though this is not the intended use).
64+
65+
To simplify voice preparation, the script automatically converts `.mp3` (or `.wav`) files to mono, 44.1 kHz, and trims them to a maximum of 15 seconds if needed. This ensures compatibility with the model’s requirements, while allowing the use of basic voice recordings—even those made quickly with a simple microphone.
66+
67+
This system makes the creation of voice dialogues fully automatable. You only need to prepare a text script with character names, add a short voice clip for each, and run the generator to get a credible mock meeting in just a few minutes.
68+
69+
<hr class="hr-text" data-content="Pitfalls">
70+
71+
## Pitfalls Encountered and Necessary Adjustments
72+
73+
Setting up an audio meeting generator seemed simple on paper… but several technical details quickly forced me to adjust my approach. Here are some of the obstacles I encountered and how I worked around them.
74+
75+
### 1. Only One Default Voice Available
76+
When testing the initial OuteTTS model, I quickly realized it only offers one default voice profile (`en-female-1-neutral`). Any attempt to load a different voice returned an error.
77+
78+
**Solution**: use custom audio files for each speaker. This allowed me to dynamically generate as many profiles as needed from my own clips (`marc.mp3`, `julie.wav`, etc.).
79+
80+
### 2. Audio Format: Tensor or WAV?
81+
Contrary to what one might expect, the `output.audio` returned by OuteTTS **is not always a WAV file**. It’s actually a **raw tensor** (typically a `numpy.ndarray` or `torch.Tensor`) representing the audio signal.
82+
83+
**Solution**: instead of treating the stream as an encoded file (which caused an error like “a bytes-like object is required”), I simply converted the tensor to a NumPy array using `np.asarray(output.audio, dtype='float32').flatten()`. This resolved the issue and enabled proper segment concatenation.
84+
85+
### 3. Slow, Deep, or Distorted Voice
86+
During my initial tests, the generated voice sounded odd: slow, deep, almost distorted. In fact, this was due to an **incorrect sample rate** during the final file recording.
87+
88+
**Solution**: manually set the `sample_rate` to **44,100 Hz**, which is the expected output rate for natural-sounding audio with OuteTTS.
89+
90+
### 4. Incompatibility with NumPy 2.0
91+
A NumPy update introduced a `DeprecationWarning` related to the use of the `copy` parameter when converting to arrays. While not blocking, it cluttered the console output.
92+
93+
**Solution**: avoid `np.array(..., copy=True)` and prefer `np.asarray(...).copy()` to ensure compatibility with NumPy 2.0 while keeping the code clean.
94+
95+
### 5. Automatically Trimming Long Audio Files
96+
Some of my audio sources exceeded 30 seconds, which caused issues for OuteTTS in generating reliable voice profiles. While the model automatically trims to 15 seconds, it’s better to handle this beforehand.
97+
98+
**Solution**: I integrated the `pydub` library to automatically convert and trim any `.mp3` or `.wav` file to 15 seconds, in mono and at 44.1 kHz. This step ensures profiles are compatible, even from imperfect audio files.
99+
100+
These small adjustments helped stabilize the tool and achieve smooth, reliable generation—especially reusable across different testing contexts. And as often with generative AI, the devil is in the details.
101+
102+
103+
<hr class="hr-text" data-content="Conclusion">
104+
105+
## Conclusion
106+
107+
By combining a simple text file, a few audio clips, and an open-source model like OuteTTS, I was able to create a tool capable of automatically generating realistic voice conversations to test my meeting transcription and analysis pipeline.
108+
109+
This generator allows me to simulate multi-speaker exchanges, vary the voices, and produce as many test cases as needed—without relying on real recordings or involving other people.
110+
111+
Beyond testing, this tool opens the door to other applications: voice rendering of meeting summaries, synthetic audio playback of sessions, or even on-the-fly simulated dialogue generation for learning environments or conversational agent prototyping.
112+
113+
The full code is open source. If you're interested in this kind of approach, or if you're developing transcription tools, TTS systems, or voice assistants, I’d be glad to connect and discuss.
488 KB
Loading

0 commit comments

Comments
 (0)