Synthèse vocale et .Net 3.0 : donnez la parole à vos applications sous Vista

Ronald VASSEUR

Au cours de cet article, je vais vous présenter un aspect souvent peu abordé, il s'agit de la synthèse vocale. En effet, peu de développeurs ont eu un jour l'idée d'intégrer une telle fonctionnalité dans leur application… Pourtant, si l'on y réfléchit un peu cela pourrait s'avérer utile dans de nombreux domaines allant de la borne de musée interactive à une application de supervision d'un quelconque process. Quand un avertissement sonore doit être émis par une application la méthode le plus souvent employée est la lecture d'un fichier préenregistré, ici je vous propose de synthétiser de manière dynamique le message vocal. Pour réaliser cela, vous allez voir que Microsoft nous simplifie grandement la vie. Depuis plusieurs années Microsoft propose gratuitement au téléchargement un SDK de reconnaissance et de synthèse vocale, depuis Windows Vista la donne a légèrement changé puisque tout cela est intégré au Framework .Net 3.0… Cet article porte uniquement sur le développement d'applications bâties autour de .Net 3.0, et donc ne s'appuyant pas sur la version 5.1 de SAPI (Speech API) employée pour les versions précédentes de Windows, mais sur la toute nouvelle version 5.3.

- Titre : Synthèse vocale et .Net 3.0 : donnez la parole à vos applications sous Vista

- Auteur : Ronald VASSEUR

- Parution : 19 juillet 2007

- Mise à jour : 29 février 2020

- Licence : Copyright ® 2007 Ronald Vasseur. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Introduction

- I. Présentation de System.Speech

- II. Donnons la parole à notre application

- III. Allons plus loin

- III-A. Choisir la voix à utiliser

- III-B. Utilisation de la classe PromptBuilder

- III-C. Persistance d'une voix synthétisée

- Conclusion

- Ressources

Introduction

Il faut tout d'abord bien séparer synthèse et reconnaissance vocale. La synthèse est la génération d'une voix synthétique à partir d'un texte donné, alors que la reconnaissance consiste à reconnaitre et à retranscrire sous forme textuelle la parole d'un utilisateur. Bien que la solution de Microsoft sache faire les deux, nous allons ici nous intéresser à la synthèse, la reconnaissance fera l'objet d'un prochain article.

Remarque : la technologie de synthèse vocale proposée par Microsoft comporte à l'heure actuelle uniquement une voix anglaise et une voix chinoise, donc pour le français il faudra patienter encore un peu, en espérant que Microsoft ne nous fera pas trop attendre. Sous Vista, la voix n'est désormais plus masculine, le bon vieux Sam a été remplacé par Anna, et Lili est la voix chinoise de Vista.

Le code sera proposé en VB.Net et en C#, et je vous le rappelle bien que basé sur le Framework .Net 3.0 ce qui sera abordé dans cet article ne sera garanti que pour un fonctionnement sous Vista, en effet la version de SAPI sur laquelle repose le code de cet article est la 5.3, qui est uniquement disponible sur le nouveau système d'exploitation de la firme de Redmond. D'ailleurs Vista utilise cette version 5.3 pour ses fonctionnalités de reconnaissance et de synthèse vocale.

I - Présentation de System.Speech

Une fois n'est pas coutume, en .Net tout se passe dans des namespaces, ici il se nomme System.Speech. Il contient tout ce dont nous aurons besoin pour synthétiser une voix et ainsi donner la parole à notre application. Vous devez donc tout d'abord ajouter une référence vers l'assembly le contenant, il s'agit de System.Speech.dll. Il ne vous reste plus qu'à ajouter Imports System.Speech.Synthetizer dans votre fichier de code pour ne pas avoir à taper à chaque fois les noms complets des objets que vous allez utiliser.

Ce namespace s'appuie sur la bibliothèque COM SAPI 5.3 et en reprend la quasi-totalité des fonctionnalités, mais quelques-unes ne sont pas présentes, je vous renvoie à la documentation officielle pour en avoir la liste. Si vous êtes adepte comme moi de .Net et pas spécialement de COM je vous recommande d'utiliser System.Speech.Synthetizer, les quelques fonctionnalités absentes par rapport a la bibliothèque COM ne sont pas vitales.

Voici les classes majeures de ce namespace.

- SpeechSynthetizer : c'est la classe la plus importante. Elle contient toutes les méthodes permettant de synthétiser la voix.

- PromptBuilder : permet de créer un document qui va servir à synthétiser la voix par l'intermédiaire de SpeechSynthetizer. Cette classe permet aussi de sérialiser ce document au format SSML.

- PromptStyle : cette classe permet de définir un style à appliquer à un document PromptBuilder, cela pour comprendre la vitesse, le volume, la prononciation…

II - Donnons la parole à notre application

Commençons par l'exemple le plus simple qui soit, le célébrissime « Hello World ». Nous allons tout d'abord ajouter une référence vers System.Speech.dll, puis ajouter Imports System.Speech.Synthetizer dans notre classe. À ce stade il nous faut désormais instancier un objet de type SpeechSynthetizer, c'est lui que nous allons utiliser pour synthétiser la voix, pour cela cette classe possède la méthode Speak() qui prend en paramètre le texte qui doit être synthétisé. Voici le code qui nous permet de faire parler notre application :

Hello World en VB.Net

Dim s As New SpeechSynthesizer
s.Speak("Hello World")

Hello World en C#

SpeechSynthesizer s = new SpeechSynthesizer();
s.Speak("Hello World");

Comme vous pouvez le voir, c'est d'une complexité folle ! Placez ce code par exemple dans l'événement Click d'un bouton et il ne reste plus qu'à écouter la voix « mélodieuse » de votre ordinateur.

Mais voyons plus en détail ce qu'il est possible de faire grâce au Framework 3.0 et plus particulièrement au namespace System.Speech, vous allez voir c'est très sympathique et cela peut apporter un vrai plus à vos applications.

III - Allons plus loin

Jusqu'ici nous nous sommes contentés de synthétiser de la voix sans réel contrôle. Nous allons voir que nous pouvons par exemple enregistrer cette voix dans un fichier, gérer les paramètres de prononciation, le volume, la vitesse. Une des nouveautés et améliorations de SAPI en version 5.3 est le support du standard W3C SSML (Speech Synthesis Markup Language) qui permet d'établir facilement les paramètres (vitesse, prononciation, volume, pauses, intonations…) de la voix synthétisée. SSML s'appuie sur un ensemble de balises à la manière de HTML/XML. Ainsi, au lieu de passer une simple chaine de caractères nous passons une chaine au format SSML qui contient tous les paramètres souhaités. Un exemple de chaine au format SSML pourrait être le suivant :

Exemple de code SSML

<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="fr-FR">Hello world, welcome on the<sub alias="dot net">.Net</sub>planet!</speak>

Ne nous attardons pas plus sur SSML pour le moment, nous y reviendrons plus loin dans cet article.

III-A - Choisir la voix à utiliser

Ici pas de longs discours non plus, car tout d'abord c'est très simple à réaliser et surtout le choix n'est pas des plus larges, en effet pour l'instant il n'existe sous Vista qu'Anna pour l'anglais, Lili pour le chinois (à condition que votre Vista dispose du bon pack de langage), et Sam pour l'anglais sous XP, ce qui n'est pas dans le cadre de cet article. Cependant on peut espérer que Microsoft élargira ce choix, et de plus il toujours est possible d'ajouter si besoin de nouvelles voix à votre système d'exploitation, des éditeurs de logiciels spécialisés en proposent. Voici comment procéder depuis votre application .Net :

Choisir la voix à utiliser en VB.Net

Private Sub SelectVoice(ByVal name As String)

' La seule voix actuellement disponible sous Vista Fr est "Microsoft Anna"
Dim s As New SpeechSynthesizer
s.SelectVoice(name)
s.Speak("Hello, my name is Anna")

End Sub

Choisir la voix à utiliser en C#

private void SelectVoice(string name)
{

// La seule voix actuellement disponible sous Vista Fr est "Microsoft Anna"
SpeechSynthesizer s = new SpeechSynthesizer();
s.SelectVoice(name);
s.Speak("Hello, my name is Anna");

}

III-B - Utilisation de la classe PromptBuilder

Si l'on veut éviter d'avoir à écrire cette chaine un peu barbare sans aucune assistance nous pouvons utiliser la classe PromptBuilder qui permet entre autres de générer une chaine SSML, mais aussi de définir les paramètres que l'on veut imposer à la voix synthétisée. En fait, PromptBuilder permet de lire, de générer du SSML, mais aussi de prendre en entrée des chaines de caractères simples, des chaines utilisant l'alphabet phonétique, etc. Il s'agit de la pierre angulaire de System.Speech lorsque l'on veut synthétiser de la voix de manière précise. Un exemple valant souvent mieux qu'un long discours voici ce qu'il est possible de faire en mettant en œuvre certaines possibilités de PromptBuilder.

Exemple d'utilisation de PromptBuilder en VB.Net

Private Sub PromptBuilderSample()

Dim p As New PromptBuilder()

Dim s1 As New PromptStyle
s1.Volume = PromptVolume.ExtraLoud
s1.Rate = PromptRate.Slow
s1.Emphasis = PromptEmphasis.Reduced

p.StartStyle(s1)

p.StartParagraph()
p.StartSentence()
p.AppendText("Hello world, its's")
p.AppendTextWithHint("07/10/2007", SayAs.Date)
p.EndSentence()
p.EndParagraph()

p.EndStyle()

p.AppendBreak(New TimeSpan(10000000))

Dim s2 As New PromptStyle
s2.Rate = PromptRate.Medium
s2.Volume = PromptVolume.ExtraLoud
s2.Emphasis = PromptEmphasis.Reduced

p.StartStyle(s2)

p.StartParagraph()
p.AppendText("welcome on the")
p.AppendTextWithAlias(".Net", "dot net")
p.AppendText("planet!")
p.EndParagraph()

p.StartParagraph()
p.StartSentence()
p.AppendTextWithHint("VB", SayAs.SpellOut)
p.AppendText("2005 is really a wonderful language !")
p.EndSentence()

p.StartSentence()
p.AppendTextWithHint("SSML", SayAs.SpellOut)
p.AppendText("standard is now included in SAPI")
p.AppendTextWithHint("5.3", SayAs.SpellOut)
p.EndSentence()
p.EndParagraph()

p.EndStyle()

p.AppendBreak(New TimeSpan(10000000))

p.AppendText("Please, visit my website at", PromptRate.Slow)
p.AppendTextWithHint("http://", SayAs.SpellOut)
p.AppendText("webman dot developpez dot com")

Dim synth As New SpeechSynthesizer
synth.SetOutputToWaveFile("c:\voix.wav")
synth.Speak(p)
synth.SetOutputToNull()

End Sub

Exemple d'utilisation de PromptBuilder en C#

private void PromptBuilderSample()
{

PromptBuilder p = new PromptBuilder();

PromptStyle s1 = new PromptStyle();
s1.Volume = PromptVolume.ExtraLoud;
s1.Rate = PromptRate.Slow;
s1.Emphasis = PromptEmphasis.Reduced;

p.StartStyle(s1);

p.StartParagraph();
p.StartSentence();
p.AppendText("Hello world, its's");
p.AppendTextWithHint("07/10/2007", SayAs.Date);
p.EndSentence();
p.EndParagraph();

p.EndStyle();

p.AppendBreak(new TimeSpan(10000000));

PromptStyle s2 = new PromptStyle();
s2.Rate = PromptRate.Medium;
s2.Volume = PromptVolume.ExtraLoud;
s2.Emphasis = PromptEmphasis.Reduced;

p.StartStyle(s2);

p.StartParagraph();
p.AppendText("welcome on the");
p.AppendTextWithAlias(".Net", "dot net");
p.AppendText("planet!");
p.EndParagraph();

p.StartParagraph();
p.StartSentence();
p.AppendTextWithHint("VB", SayAs.SpellOut);
p.AppendText("2005 is really a wonderful language !");
p.EndSentence();

p.StartSentence();
p.AppendTextWithHint("SSML", SayAs.SpellOut);
p.AppendText("standard is now included in SAPI");
p.AppendTextWithHint("5.3", SayAs.SpellOut);
p.EndSentence();
p.EndParagraph();

p.EndStyle();

p.AppendBreak(new TimeSpan(10000000));

p.AppendText("Please, visit my website at", PromptRate.Slow);
p.AppendTextWithHint("http://", SayAs.SpellOut);
p.AppendText("webman dot developpez dot com");

SpeechSynthesizer synth = new SpeechSynthesizer();
synth.SetOutputToWaveFile("c:\\voix.wav");
synth.Speak(p);
synth.SetOutputToNull();

}

Voici quelques membres de la classe PromptBuilder qui méritent que l'on s'attarde dessus. Cette liste n'est pas exhaustive, et je vous renvoie donc vers la documentation officielle dont le lien est disponible en fin d'article dans la partie « Ressources ».

- AppendAudio() : cette méthode permet d'ajouter le contenu d'un fichier wav à notre objet PromptBuilder.

- AppendText() : cette méthode permet d'ajouter un texte simple à synthétiser, on peut passer en plus à la méthode la vitesse de diction souhaitée pour ce texte.

- AppendTextWithHint() : cette méthode permet également d'ajouter du texte, mais en plus elle permet de définir la manière de prononcer ce texte. Une énumération (SayAs) nous permet de choisir parmi une liste de prononciation définie, cela sera par exemple épeler le texte, le prononcer comme une date ou comme une heure…

- AppendTextWithAlias() : comme les deux précédentes cette méthode permet d'ajouter du texte à synthétiser, mais elle permet de définir un alias qui correspondra à la manière de prononcer le texte qui peut être différente de la manière dont il est écrit. Un exemple simple pourrait être « .Net » que l'on veut prononcer comme « dot net ».

- AppendBreak() : cette méthode permet de marquer une pause, une rupture dans la lecture, qui peut être plus ou moins longue.

- StartStyle() / End Style() : permettent de définir un style (vitesse, volume, emphase…) pour le texte inclus entre ces deux méthodes. L'exemple que je donne un peu plus haut vous donne un bon aperçu de ce qu'il est possible de faire avec un objet PromptStyle.

- StartParagraph()/EndParagraph() : permettent de délimiter un paragraphe.

- StartSentence()/EndSentence() : permettent de délimiter une phrase.

- SayAs : cette énumération contient les prononciations prédéfinies que l'on peut entre autres spécifier en paramètre de la méthode AppendTextWithHint() que l'on vient de voir. Voici le contenu exhaustif de cette énumération : Date, Day, DayMonth, DayMonthYear, Month, MonthDay, MonthDayYear, MonthYear, NumberCardinal, NumberOrdinal, SpellOut, Telephone, Text, Time, Time12, Time24, Year, YearMonth, YearMonthDay

Je vous propose maintenant de voir comment PromptBuilder peut générer du SSML ou prendre du SSML en entrée. Tout d'abord, pour générer du SSML il faut utiliser la méthode ToXml() de la classe PromptBuilder, ça n'est pas plus compliqué que ça. Cette méthode retourne une chaine contenant le code SSML représentant la voix que l'on souhaite synthétiser. Si l'on veut conserver ces données SSML dans un fichier, il suffit d'utiliser un objet StreamWriter.

Exporter la chaine SSML directement dans un fichier en VB.Net

Private Sub PromptBuilderToSsmlFile()

Dim p As New PromptBuilder()
p.AppendText("Hello world, welcome on the")
p.AppendTextWithAlias(".Net", "dot net")
p.AppendText("planet!")

Using sw As New StreamWriter("c:\monfichier.ssml")
sw.Write(p.ToXml())
End Using

End Sub

Exporter la chaine SSML directement dans un fichier en C#

private void PromptBuilderToSsmlFile()
{

PromptBuilder p = new PromptBuilder();
p.AppendText("Hello world, welcome on the");
p.AppendTextWithAlias(".Net", "dot net");
p.AppendText("planet!");

using (StreamWriter sw = new StreamWriter("c:\\monfichier.ssml")) {
sw.Write(p.ToXml());
}

}

Remarque : attention, j'utilise ici Using qui s'occupe de la gestion du StreamWriter, dans d'autres circonstances il faudrait penser à fermer le StreamWriter avec la méthode Close().

Voyons comment maintenant importer du SSML directement dans notre objet PromptBuilder. Pour cela il existe deux méthodes qui sont AppendSsml() et AppendSsmlMarkup(). La première prend en paramètre le chemin d'un fichier contenant le code SSML, la deuxième quant à elle prend en directement en paramètre une chaine au format SSML. Voici un exemple d'utilisation de ces méthodes.

***

Private Sub AppendSsml()

Dim p As New PromptBuilder()
p.AppendSsml("c:\fichier.ssml")
p.AppendSsmlMarkup("remplacer ce texte par votre chaine SSML")

Dim s As New SpeechSynthesizer
s.Speak(p)

End Sub

***

private void AppendSsml()
{

PromptBuilder p = new PromptBuilder();
p.AppendSsml("c:\\fichier.ssml");
p.AppendSsmlMarkup("remplacer ce texte par votre chaine SSML");

SpeechSynthesizer s = new SpeechSynthesizer();
s.Speak(p);

}

Remarque : dans cette partie nous venons de voir que l'objet PromptBuilder est très souple en termes de sources de texte à synthétiser, cela peut être des chaines des caractères simples, du SSML, du wav, en flux, en fichier, etc. Et tout cela est possible pour un même objet, il est donc envisageable de combiner différentes sources sans pour autant avoir à les contraindre préalablement dans un format donné. À l'usage cela se révèle très appréciable.

Voyons maintenant comment faire persister la voix que l'on a synthétisée.

III-C - Persistance d'une voix synthétisée

Si l'on veut faire persister une voix synthétisée ou pourquoi pas l'échanger entre différentes machines et pourquoi pas plateformes, nous avons deux possibilités principales : enregistrer la voix dans un fichier Wav (le format étant supporté sur de nombreuses plateformes) ou la sérialiser au format SSML. La deuxième solution est évidemment dans la majorité des situations la meilleure, d'autant plus que SSML est un standard W3C largement adopté.

Enregistrement de la voix dans un fichier Wav :

Synthétiser un PromptBuilder dans un fichier Wav en VB.Net

Private Sub PromptBuilderToWavFile()

Dim p As New PromptBuilder
p.AppendTextWithHint("VB", SayAs.SpellOut)
p.AppendTextWithAlias(".Net", "dot net")
p.AppendText("is the nicest language for .Net platform !")

Dim synth As New SpeechSynthesizer
synth.SetOutputToWaveFile("c:\voix.wav")
synth.Speak(p)
synth.SetOutputToNull()

End Sub

Synthétiser un PromptBuilder dans un fichier Wav en C#

private void PromptBuilderToWavFile()
{

PromptBuilder p = new PromptBuilder();
p.AppendTextWithHint("VB", SayAs.SpellOut);
p.AppendTextWithAlias(".Net", "dot net");
p.AppendText("is the nicest language for .Net platform !");

SpeechSynthesizer synth = new SpeechSynthesizer();
synth.SetOutputToWaveFile("c:\\voix.wav");
synth.Speak(p);
synth.SetOutputToNull();

}

Sérialisation SSML de la voix :

Sérialisation d'un PromptBuilder en VB.Net

Private Function PromptBuilderToSsml() As String

Dim p As New PromptBuilder
p.AppendTextWithHint("VB", SayAs.SpellOut)
p.AppendTextWithAlias(".Net", "dot net")
p.AppendText("is the nicest language for .Net platform !")

Return p.ToXml

End Function

Sérialisation d'un PromptBuilder en C#

private string PromptBuilderToSsml()
{

PromptBuilder p = new PromptBuilder();
p.AppendTextWithHint("VB", SayAs.SpellOut);
p.AppendTextWithAlias(".Net", "dot net");
p.AppendText("is the nicest language for .Net platform !");

return p.ToXml;

}

Remarque : attention au wav, il s'agit d'un format non compressé, et donc le volume du fichier peut rapidement augmenter, même s'il est possible d'ajuster les paramètres d'enregistrement il faudra garder à l'esprit ce détail. Voici un argument de plus en faveur d'une sérialisation SSML de la voix, plutôt que d'un enregistrement dans un fichier wav.

Info : si vous souhaitez en savoir plus sur le SSML un lien vers la documentation de ce format est disponible dans la partie « Ressources » de cet article.

Conclusion

J'espère que cet article vous aura donné l'envie de tester la synthèse vocale sous Vista, et pourquoi pas même de l'intégrer dans vos prochaines applications. Il serait cependant souhaitable que Microsoft ajoute de nouvelles voix en plus de celles de Lili et d'Anna. Nous avons vu que System.Speech comporte une partie synthèse, mais aussi une partie reconnaissance vocale qui fera l'objet d'un prochain article.

Un grand merci à AGM26 pour la relecture de cet article, ainsi qu'à l'équipe Dotnet pour son aide.

Ressources

Namespace System.Speech.Synthesis
Documentation du langage SSML sur le site du W3C

Mon blog
Mes articles
Les articles des autres rédacteurs
La rubrique .Net de Developpez.com
La rubrique Sécurité de Developpez.com