numeriser_jean_de_la_fontaine
Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
numeriser_jean_de_la_fontaine [2022/03/04 15:25] – Simon Deplat | numeriser_jean_de_la_fontaine [2022/03/07 16:10] (Version actuelle) – Simon Deplat | ||
---|---|---|---|
Ligne 27: | Ligne 27: | ||
**//pip// permet d' | **//pip// permet d' | ||
- | Après cela, une fois dans le bon dossier ( par exemple / | + | Après cela, une fois dans le bon dossier ( par exemple |
<code bash> | <code bash> | ||
git clone https:// | git clone https:// | ||
Ligne 95: | Ligne 95: | ||
from aitextgen import aitextgen | from aitextgen import aitextgen | ||
- | ai2 = aitextgen(model_folder=" | + | ai = aitextgen(model_folder=" |
tokenizer_file=" | tokenizer_file=" | ||
- | ai2.generate(1, | + | ai.generate(1, |
</ | </ | ||
- | Pour l' | + | Pour l' |
<code bash> | <code bash> | ||
- | ./ | + | ./ |
</ | </ | ||
- | Un exemple de texte généré : | + | **Un exemple de texte généré** : |
< | < | ||
Ligne 118: | Ligne 118: | ||
**C' | **C' | ||
- | ** | + | |
- | [EN CONSTRUCTION]** | + | =====Vocalisation===== |
+ | |||
+ | Maintenant que le plus important est fait, à savoir, d'une certaine manière, avoir mis en place le contexte cérébral de ce petit Jean, il faut commencer à l'**incarner**. | ||
+ | |||
+ | La première étape est de pouvoir **le faire dicter ses nouvelles fables**, en utilisant **un logiciel de synthèse sonore**, en anglais //Text To Speech//, souvent abrévié //TTS//. | ||
+ | |||
+ | **La ressource principale** que j'ai trouvée, sur ce sujet et concernant les logiciels libres, est [[https:// | ||
+ | |||
+ | De manière succincte, voici les résultats auxquels j'ai abouti pour ces logiciels. Je n'ai pas essayé // | ||
+ | |||
+ | Premièrement, | ||
+ | |||
+ | < | ||
+ | espeak -v fr -f text.txt | ||
+ | </ | ||
+ | |||
+ | Concernant // | ||
+ | |||
+ | < | ||
+ | pico2wave -l fr-FR -w text.wav < text.txt && play text.wav | ||
+ | </ | ||
+ | |||
+ | Avec //Mozilla TTS//, **les résultats sont plus probants**, même s'il y a quelques cafouillis par-ci par-là. Mais là encore, **seules des voix féminines sont disponibles** : | ||
+ | |||
+ | < | ||
+ | tts --text "$(cat text.txt)" | ||
+ | </ | ||
+ | |||
+ | **De fait, la solution que j'ai finalement retenue se sert d'// | ||
+ | |||
+ | < | ||
+ | sudo apt-get install mbrola mbrola-fr* | ||
+ | </ | ||
+ | |||
+ | Cette fois-ci, **nous utilisons //espeak// pour générer un fichier de phonèmes, puis nous utilisons les vocoders de //mbrola// pour les lire** : | ||
+ | |||
+ | |||
+ | < | ||
+ | espeak -q -s 100 -v mb/mb-fr1 -f text.txt --pho --phonout=text.pho && mbrola / | ||
+ | </ | ||
+ | |||
+ | Étrangement, | ||
+ | |||
+ | **Pour changer de vocoder**, il faut **spécifier le chemin de celui-ci** dans la commande //mbrola//. Dans le cas précédent, | ||
+ | |||
+ | Cette fois-ci, nous avons **une voix masculine d' | ||
+ | |||
+ | =====Quoi ma gueule ?===== | ||
+ | |||
+ | Il faut maintenant associer la voix à un visage. Pour ce faire, **un petit tour sur le net et un copier-coller** d'un tableau représentatif de l' | ||
+ | |||
+ | Mais ce n'est pas tout, j' | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | =====LaFontaine.exe===== | ||
+ | |||
+ | Il est maintenant temps de regrouper ensemble les pensées, la voix et la frimousse de Jean ! | ||
+ | |||
+ | J' | ||
+ | |||
+ | Voici le code du projet : | ||
+ | |||
+ | < | ||
+ | ( | ||
+ | |||
+ | var win = Window.new( | ||
+ | "La Fontaine", | ||
+ | Rect( 1000, 500, 200, 200 ) ); | ||
+ | |||
+ | var filePath = thisProcess.nowExecutingPath.dirname ++ "/ | ||
+ | |||
+ | var lock = false; // Empêche de regénérer une fable tant que la précédente n'est pas finie | ||
+ | var lockDelay = 1; // Délai supplémentaire de restriction | ||
+ | |||
+ | var stringCmd; // Variable qui va stocker l' | ||
+ | |||
+ | var view = UserView(); | ||
+ | |||
+ | var imgJean = Image.new( | ||
+ | thisProcess.nowExecutingPath.dirname ++ "/ | ||
+ | var imgEmpty = Image.new( | ||
+ | thisProcess.nowExecutingPath.dirname ++ "/ | ||
+ | var imgChin = Image.new( | ||
+ | thisProcess.nowExecutingPath.dirname ++ "/ | ||
+ | var imgPupille = Image.new( | ||
+ | thisProcess.nowExecutingPath.dirname ++ "/ | ||
+ | |||
+ | var textSpeed = 120; // Vitesse d' | ||
+ | var voiceTag = " | ||
+ | |||
+ | var jawOffset = 0; // Décalage de la mâchoire | ||
+ | |||
+ | var eg = Point( 390, 512 ); // Position de l'oeil gauche | ||
+ | var ed = Point( 385, 415 ); // Position de l'oeil droit | ||
+ | |||
+ | var egtarget = Point( 390, 512 ); // Position de référence l'oeil gauche | ||
+ | var edtarget = Point( 385, 415 ); // Position de référence l'oeil droit | ||
+ | |||
+ | if( ~masterIn == nil, { // Bus master | ||
+ | ~masterIn = Bus.audio( s, 1 ) } ); | ||
+ | |||
+ | if( ~ampBus == nil, { // Bus de valeur d' | ||
+ | ~ampBus = Bus.control( s, 1 ) } ); | ||
+ | |||
+ | |||
+ | SynthDef( \master, { | ||
+ | |||
+ | var sound = In.ar( ~masterIn, 1 ); | ||
+ | var amp = Amplitude.kr( sound ); | ||
+ | var panSound = Pan2.ar( sound ); | ||
+ | |||
+ | Out.kr( ~ampBus, amp ); | ||
+ | Out.ar( 0, panSound ); | ||
+ | |||
+ | } ).play; | ||
+ | |||
+ | |||
+ | // Construction de la commande Bash | ||
+ | voiceTag = voiceTag ++ "/" | ||
+ | |||
+ | stringCmd = "cd ~/ | ||
+ | stringCmd = stringCmd ++ " | ||
+ | stringCmd = stringCmd ++ " | ||
+ | stringCmd = stringCmd ++ " | ||
+ | |||
+ | // Fonction déclenchée à l' | ||
+ | win.view.keyDownAction = { arg view, char, modifiers, unicode, | ||
+ | |||
+ | if( lock == false, { | ||
+ | if( unicode == 32, { | ||
+ | |||
+ | stringCmd.systemCmd; | ||
+ | lock = true; | ||
+ | |||
+ | b = Buffer.read( | ||
+ | s, | ||
+ | filePath, | ||
+ | action: { { lock = false; }.defer( b.numFrames * ( 1.0 / b.sampleRate ) ) } | ||
+ | ); | ||
+ | |||
+ | SynthDef( " | ||
+ | Out.ar( ~masterIn, | ||
+ | PlayBuf.ar( | ||
+ | numChannels: | ||
+ | bufnum: | ||
+ | rate: | ||
+ | trigger: | ||
+ | startPos: | ||
+ | loop: | ||
+ | doneAction: | ||
+ | 0.0 | ||
+ | ) | ||
+ | } ).play; | ||
+ | } ); | ||
+ | } ); | ||
+ | }; | ||
+ | |||
+ | win.view.layout_( HLayout() ); | ||
+ | |||
+ | win.view.layout.add( view ); | ||
+ | |||
+ | // Fonction d' | ||
+ | view.drawFunc = { | ||
+ | Pen.drawImage( Point( 1920 - 1284 / 2 + 350, 1080 - 1000 / 2 + ( 1000 - 437 - 175 ) ), imgEmpty, operation: ' | ||
+ | |||
+ | |||
+ | |||
+ | Pen.drawImage( Point( 1920 - 1284 / 2 + eg.x, 1080 - 1000 / 2 + eg.y ), imgPupille, operation: ' | ||
+ | |||
+ | Pen.drawImage( Point( 1920 - 1284 / 2 + ed.x, 1080 - 1000 / 2 + ed.y ), imgPupille, operation: ' | ||
+ | |||
+ | Pen.drawImage( Point( 1920 - 1284 / 2, 1080 - 1000 / 2 ), imgJean, operation: ' | ||
+ | Pen.drawImage( Point( 1920 / 2 - 132 + jawOffset, 1080 / 2 - 83 ), imgChin, operation: ' | ||
+ | }; | ||
+ | |||
+ | |||
+ | // Routine qui associe l' | ||
+ | Routine( { | ||
+ | |||
+ | var rand; | ||
+ | var randN = 10; | ||
+ | |||
+ | loop { | ||
+ | |||
+ | ~ampBus.get( { | val | jawOffset = val.linlin( 0.0, 1.0, 0, 100 ); } ); | ||
+ | jawOffset = jawOffset.asInteger; | ||
+ | |||
+ | if( lock == true, { | ||
+ | rand = randN.rand; | ||
+ | if( rand == 0, { eg.x = eg.x + 1 } ); | ||
+ | if( rand == 1, { eg.x = eg.x - 1 } ); | ||
+ | if( eg.x < 385, { eg.x = 385 } ); | ||
+ | if( eg.x > 410, { eg.x = 410 } ); | ||
+ | |||
+ | rand = randN.rand; | ||
+ | if( rand == 0, { eg.y = eg.y + 1 } ); | ||
+ | if( rand == 1, { eg.y = eg.y - 1 } ); | ||
+ | if( eg.y < 488, { eg.y = 488 } ); | ||
+ | if( eg.y > 524, { eg.y = 524 } ); | ||
+ | |||
+ | rand = randN.rand; | ||
+ | if( rand == 0, { ed.x = ed.x + 1 } ); | ||
+ | if( rand == 1, { ed.x = ed.x - 1 } ); | ||
+ | if( ed.x < 380, { ed.x = 380 } ); | ||
+ | if( ed.x > 395, { ed.x = 395 } ); | ||
+ | |||
+ | rand = randN.rand; | ||
+ | if( rand == 0, { ed.y = ed.y + 1 } ); | ||
+ | if( rand == 1, { ed.y = ed.y - 1 } ); | ||
+ | if( ed.y < 400, { ed.y = 400 } ); | ||
+ | if( ed.y > 430, { ed.y = 430 } ); | ||
+ | } ); | ||
+ | |||
+ | if( lock == false, { | ||
+ | if( eg.x < egtarget.x, { eg.x = eg.x + 1 } ); | ||
+ | if( eg.x > egtarget.x, { eg.x = eg.x - 1 } ); | ||
+ | if( eg.y < egtarget.y, { eg.y = eg.y + 1 } ); | ||
+ | if( eg.y > egtarget.y, { eg.y = eg.y - 1 } ); | ||
+ | |||
+ | if( ed.x < edtarget.x, { ed.x = ed.x + 1 } ); | ||
+ | if( ed.x > edtarget.x, { ed.x = ed.x - 1 } ); | ||
+ | if( ed.y < edtarget.y, { ed.y = ed.y + 1 } ); | ||
+ | if( ed.y > edtarget.y, { ed.y = ed.y - 1 } ); | ||
+ | } ); | ||
+ | |||
+ | { view.refresh }.defer; | ||
+ | |||
+ | (1/ | ||
+ | }; | ||
+ | } ).play; | ||
+ | |||
+ | win.background = Color.black; | ||
+ | win.fullScreen; | ||
+ | |||
+ | win.front; | ||
+ | |||
+ | // À la fermeture, libération de la mémoire : | ||
+ | CmdPeriod.doOnce( { | ||
+ | Window.closeAll; | ||
+ | s.freeAll; | ||
+ | imgChin.free; | ||
+ | imgJean.free; | ||
+ | imgEmpty.free; | ||
+ | imgPupille.free; | ||
+ | } ); | ||
+ | |||
+ | ) | ||
+ | </ | ||
+ | |||
+ | Sans rentrer trop dans le détail, quelques points sont intéressants à aborder sur l' | ||
+ | |||
+ | ===Corrélation de l' | ||
+ | |||
+ | L' | ||
+ | |||
+ | Au lancement du programme, **un synthé maître est créé**, qui utilise un //Ugen IN// afin de pouvoir bénéficier d'un son d' | ||
+ | |||
+ | Dans le synthé maître, avant de **le passer en stéréo et de l' | ||
+ | |||
+ | C'est **une //Routine// qui**, à intervalle régulier, **récupère la valeur d' | ||
+ | |||
+ | La méthode d' | ||
+ | |||
+ | ===Commandes BASH depuis SC et synchronicité=== | ||
+ | |||
+ | **Sous //Linux//, il est très simple d' | ||
+ | |||
+ | < | ||
+ | " | ||
+ | </ | ||
+ | |||
+ | |||
+ | < | ||
+ | "cd Dossier/; | ||
+ | </ | ||
+ | |||
+ | Dans // | ||
+ | |||
+ | Dans notre cadre, la génération de la fable, et dans une moindre mesure la génération du fichier audio, prend un peu de temps à l' | ||
+ | |||
+ | Le cas échéant, en utilisant .// | ||
+ | |||
+ | Ici, un problème posé est que la suspension d' | ||
+ | |||
+ | Dans l' | ||
+ | |||
+ | =====La Suite ?===== | ||
+ | |||
+ | À ce stade, **le prototype est fonctionnel mais quelques améliorations sont possibles** : | ||
+ | |||
+ | * Refaire l' | ||
+ | * Mettre l' | ||
+ | * Ajouter un retour visuel de la fable générée, parce que la voix n'est pas toujours claire, et pour les publics malentendants. | ||
+ | * Réaliser le dispositif physique de l' | ||
+ | * Trouver un moyen de générer des fables plus longues. | ||
+ | |||
+ | {{tag> bestiaire_ia tal }} |
numeriser_jean_de_la_fontaine.1646407558.txt.gz · Dernière modification : 2022/03/04 15:25 de Simon Deplat