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:29] – Simon Deplat | numeriser_jean_de_la_fontaine [2024/12/10 16:05] (Version actuelle) – Benjamin Labomedia | ||
|---|---|---|---|
| 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, |
| </ | </ | ||
| Ligne 107: | Ligne 107: | ||
| </ | </ | ||
| - | Un exemple de texte généré : | + | **Un exemple de texte généré** : |
| < | < | ||
| Ligne 119: | Ligne 119: | ||
| **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. | ||
| + | | ||
| + | | ||
| + | |||
| + | {{tag> bestiaire_ia tal ia}} | ||
numeriser_jean_de_la_fontaine.1646407742.txt.gz · Dernière modification : de Simon Deplat
