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é 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é remplace 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 garantit 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.

1. Presentation 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 librairie COM SAPI 5.3 et en reprend la quasi totalité des fonctionnalités, mais cependant 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 libraire COM ne sont pas vitales.

Voici les classes majeures de ce namespace :

  • SpeechSynthetizer : c'est la classe la plus importante. Elle contient toutes les methodes permettant de synthetiser la voix.
  • PromptBuilder : permet de creer un document qui va servir a synthetiser la voix par l'intermediaire de SpeechSynthetizer. Cette classe permet aussi de serialiser ce document au format SSML.
  • PromptStyle : cette classe permet de definir un style a appliquer a un document PromptBuilder, cela pour comprendre la vitesse, le volume, la pronomciation...

2. Donnons la parole a 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. A 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
Sélectionnez

        Dim s As New SpeechSynthesizer
        s.Speak("Hello World")
Hello World en C#
Sélectionnez

		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.

3. 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élioration 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
Sélectionnez

<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.

3.1. 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 large, 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
Sélectionnez

    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#
Sélectionnez

	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");
	
	}

3.2. 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 autre 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
Sélectionnez

 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#
Sélectionnez

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 a 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 a synthétiser mais elle permet de définir un alias qui correspondra a 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 autre 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, ca n'est pas plus complique que ca. 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 simplement un objet StreamWriter.

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

    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#
Sélectionnez

	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 a 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 prends en paramètre le chemin d'un fichier contenant le code SSML, la deuxième quand a elle prend en directement en paramètre une chaine au format SSML. Voici un exemple d'utilisation de ces méthodes.

***
Sélectionnez

    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
***
Sélectionnez

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 tres 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é. A 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.

3.3. Persistance d'une voix synthetisee

Si l'on veut faire persister une voix synthétisée ou pourquoi pas l'échanger entre différentes machines et pourquoi pas plateforme, 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
Sélectionnez

    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#
Sélectionnez

	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
Sélectionnez

    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#
Sélectionnez

	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 compresse, 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