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 :
Dim
s As
New
SpeechSynthesizer
s.Speak
(
"Hello World"
)
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 :
<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 :
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
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.
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
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.
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
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 :
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
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 :
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
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.