Category Archives: French

Horloge parlante Ă  la demande

Bonjour Ă  tous!

Dans la série des projets un peu fou, je voudrais vous parler de mon horloge parlante !?

D’abord, un peu de contexte. Depuis peu, je frĂ©quente un serveur discord (The language sloth) dĂ©diĂ© Ă  l’apprentissage des langues.
Je participe Ă  ma hauteur pour aider les dĂ©butants en français. Ce n’est pas mon mĂ©tier mais l’ambiance est assez cool.

De coup, je me suis dit un peu renseigner sur les connaissances Ă  avoir pour obtenir le niveau A1 (le minimum).
Il y a comprendre l’heure. Sur ce constat, j’ai dĂ©marrĂ© un petit projet pour proposer un système capable de sortir un fichier audio pour chaque heure de la journĂ©e.
Et de proposer l’ensemble des façons de dire l’heure.

Le code:
Pour mes petits scripts, j’aime bien utiliser du python. Ici, mon petit script python permet d’enregistrer facilement les samples nĂ©cessaires pour bâtir l’ensemble de donnĂ©es.
Ce travail construit les donnĂ©es d’une voix.
Il y a 28 mots Ă  enregistrer:

[pastacode lang=”python” manual=”unity%3D%5B%22un%22%2C%22une%22%2C%22deux%22%2C%22trois%22%2C%22quatre%22%2C%22cinq%22%2C%22six%22%2C%22sept%22%2C%22huit%22%2C%22neuf%22%5D%0Atens%3D%5B%22dix%22%2C%22onze%22%2C%22douze%22%2C%22treize%22%2C%22quatorze%22%2C%22quinze%22%2C%22seize%22%2C%22vingt%22%2C%22trente%22%2C%22quarante%22%2C%22cinquante%22%5D%0Awords%3D%5B%22midi%22%2C%22minuit%22%2C%22et%22%2C%22quart%22%2C%22moins%22%2C%20%22heure%22%2C%20%22le%22%5D” message=”Les samples Ă  enregistrer” highlight=”” provider=”manual”/]

 

Le site:
Le reste de l’application est constituĂ© d’un site web qui permet de choisir une voix, et une heure.
Suite Ă  cela, le site affiche un ou deux lecteurs audios pour permettre la lecture des sons.
J’ai recyclĂ© l’architecture php de mes sites applicatifs. Je la trouve vraiment formidable. MĂŞme si, je trouve le PHP de plus en plus dĂ©gueulasse.

Pour la suite, j’aimerai bien «gamifier» la chose. Permettre d’entendre une heure et de choisir la bonne valeur parmi trois propositions. Jouer avec ce genre d’Ă©lĂ©ments.

Si vous testez le site, pensez Ă  mettre le son pas trop fort.

 

Conclusion:
J’avais cette idĂ©e de partir des samples pour construire les messages audios qui traĂ®ner dans ma tĂŞte depuis un moment. J’avais envie de tester ce que cela donnerait d’enregistrer 28 samples environ pour gĂ©nĂ©rer 1560 fichiers audios. J’ai dĂ©cidĂ© de l’Ă©crire et voilĂ  le rĂ©sultat. La curiositĂ© s’attardait sur la qualitĂ© qu’on pourrait obtenir avec cette mĂ©thode. C’est audible, il y a quelques sont qui mĂ©riterait d’ĂŞtre retravailler mais dans l’ensemble, cela remplit son rĂ´le.
Je serai ravi de recevoir de nouvelles voix. Vous pouvez m’envoyer vos 28 samples et je peux m’occuper du reste.

Les liens:
Le projets en ligne: http://heures.renaudguezennec.eu/
Le code source du projet: https://github.com/obiwankennedy/french_talking_clock_game

Gérer les préférences utilisateurs en C++

Aujourd’hui, nous allons voir comment sont gérées les préférences dans Rolisteam (et plus généralement dans mes développements).

Quels contenus sont gérés dans les Préférences ?

Les prĂ©fĂ©rences conservent l’ensemble des Ă©lĂ©ments qu’un utilisateur peut dĂ©finir pour changer le comportement de l’application.

Dans Rolisteam, il est possible de définir le thème de l’application, la couleur du brouillard de guerre pour les cartes, la valeur de son opacité, Activer/désactiver la recherche de mise à jour, le lancement en plein écran, des alias de dés etc.

La problématique

La principale difficultĂ© est de rendre accessible en lecture et en Ă©criture les prĂ©fĂ©rences pour l’ensemble de l’application.
En parallèle, la fenĂŞtre de prĂ©fĂ©rences doit permettre d’afficher et de modifier toutes les valeurs.
Il est important de ne conserver qu’une version de donnĂ©es afin d’Ă©viter des incohĂ©rences. L’autre objecti est de rĂ©ussir Ă  notifier les utilisateurs d’une donnĂ©e après son changement.

La solution

Pour répondre à ces problématiques, j’ai écrit la classe PreferencesManager. Elle est fondée sur le patron de conception: Singleton.
Le but de ce patron est de s’assurer de l’unicité d’une instance de classe. Cela permet de garantir que les valeurs sera bien la même pour tout le monde: les composants de l’application et le panneau de préférences.

Pour réaliser un singleton, il est nécessaire de mettre les éléments suivants:

[pastacode lang=”cpp” manual=”class%20PreferencesManager%0A%7B%0Apublic%3A%0A%20%20%20%20%2F**%0A%20%20%20%20*%20%40brief%20Must%20be%20called%20instead%20of%20the%20constructor.%0A%20%20%20%20*%0A%20%20%20%20*%20%40return%20instance%20of%20PreferencesManager%0A%20%20%20%20*%2F%0A%20%20%20%20static%20PreferencesManager*%20getInstance()%3B%0A%20%20%20%20%2F**%0A%20%20%20%20*%20%40brief%20%20desturctor%0A%20%20%20%20*%2F%0A%20%20%20%20~PreferencesManager()%3B%0Aprivate%3A%0A%20%20%20%20%2F**%0A%20%20%20%20*%20%40brief%20Private%20constructor%20to%20make%20sure%20there%20is%20only%20one%20instance%20of%20this.%0A%20%20%20%20*%2F%0A%20%20%20%20PreferencesManager()%3B%0Aprivate%3A%0A%20%20%20%20%2F**%0A%20%20%20%20*%20Static%20reference%2C%20part%20of%20the%20singleton%20pattern%0A%20%20%20%20*%2F%0A%20%20%20%20static%20PreferencesManager*%20m_singleton%3B%0A%7D%3B” message=”Code d’un Singleton” highlight=”” provider=”manual”/]

Côté implémentation, cela donne ceci:

[pastacode lang=”cpp” manual=”PreferencesManager*%20PreferencesManager%3A%3Am_singleton%20%3D%20NULL%3B%0A%0A%0APreferencesManager*%20PreferencesManager%3A%3AgetInstance()%0A%7B%0A%20%20%20%20if(m_singleton%20%3D%3D%20NULL)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20m_singleton%20%3D%20new%20PreferencesManager%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20return%20m_singleton%3B%0A%7D” message=”ImplĂ©mentation Singleton” highlight=”” provider=”manual”/]

La classe PreferencesManager conserve l’ensemble des donnĂ©es dans une map.

[pastacode lang=”cpp” manual=”QMap%3CQString%2CQVariant%3E*%20m_optionDictionary%3B” message=”structure de donnĂ©es” highlight=”” provider=”manual”/]

 

 

QML – Tutoriel 4 – Bindings et les mĂ©thodes de travail

Bindings + Méthode de travail:
Notions abordées:
-Les syntaxes pour le Binding

Le code de la présentation: https://github.com/obiwankennedy/Learn-Qt
Le code de l’exercice: 04_exercice_binding
La correction sera dans le prochain épisode.

 

Lien youtube: https://youtu.be/A-SMHKkH19U

Supporter le logiciel et la chaîne: https://liberapay.com/Rolisteam/donate
Rolisteam: http://www.rolisteam.org/

QML – Tutoriel 3 – Les attributs

Les attributs:
Notions abordées:
-id
-Propriétés
-Signaux
-Gestionnaire de Signaux
-Gestionnaires et propriétés groupés.
-méthodes
-Enumération

Fichier à télécharger: 03_attributs

Le code de la présentation: https://github.com/obiwankennedy/Learn-Qt

 

Lien youtube: https://youtu.be/tDSVlrsQJU0

QML – Tutoriel 2 – Le positionnement

Le positionnement:
-Apprenez à positionner vos éléments.
Notions abordées:
-Positionnement absolue
-Ancres
-Les positionneurs
-Les layouts
Le code de la présentation: https://github.com/obiwankennedy/Learn-Qt

Ressources du tutoriel: 03_positionnement

 

Lien youtube: https://youtu.be/QDbn1CDjc14

Supporter le logiciel et la chaîne: https://liberapay.com/Rolisteam/donate
Rolisteam: http://www.rolisteam.org/

QML – Tutoriel 1 – Introduction

Introduction:
-Apprenez Ă  installer les outils pour programmer en QML.
-Création d’un projet QML.
-Découverte du premier programme.
-Introduction des types de base du QML (Item, Rectangle, Image, Text, TextInput, TextArea, MouseArea, Timer, Flickable)

 

 

Téléchargez les ressources de l’épisode

Liens Utiles:
Qt: https://www.qt.io/download
RCSE: http://www.rolisteam.org/download.html (Lire attentivement en fonction de vos plate formes)
Le code de la présentation: https://github.com/obiwankennedy/Learn-Qt

Exemple QML:
https://www.youtube.com/results?search_query=qml+example

Lien youtube: https://youtu.be/8moq00ofQPY

Supporter le logiciel et la chaîne: https://liberapay.com/Rolisteam/donate
Rolisteam: http://www.rolisteam.org/

Atelier QML à Pas Sage En Seine 2017

Bonjour,

J’ai organisé un atelier à Pas Sage En Seine en juin 2017.
J’ai enfin le temps de m’occuper de mettre en ligne les éléments de formation.
Je mets Ă  disposition les supports de cet atelier.
Il y a une présentation PDF et un projet regroupant les exercices et les solutions.

La présentation est en réalité une application QML. Son code est disponible ici: https://github.com/obiwankennedy/Learn-Qt
Cela constitue en soit un bon exercice que d’arriver à la lancer sur votre ordinateur.

N’hésitez pas à me contacter pour poser des questions.
Je ferais peut-être une vidéo de la formation.

Liens:

Mettre Ă  jour Postgresql sur debian Stretch

Stretch

Nous allons voir comment mettre à jour votre base de données postgresql après l’update de Jessie à Stretch.
La version de jessie était 9.4. La version pour Stretch est 9.6.

Les commandes suivantes sont exécutées en root. Vous pouvez bien sûr utiliser sudo.

Lister les clusters postgresql installés sur votre machine:

# pg_lsclusters

Si votre installation de postgresql date de Jessie vous aurez un truc comme cela.

[pastacode lang=”bash” manual=”Ver%20Cluster%20Port%20Status%20Owner%20Data%20directory%20Log%20file%0A9.4%20main%20%20%20%205432%20online%20postgres%20%2Fvar%2Flib%2Fpostgresql%2F9.4%2Fmain%20%2Fvar%2Flog%2Fpostgresql%2Fpostgresql-9.4-main.log%0A9.6%20main%20%20%20%205433%20online%20postgres%20%2Fvar%2Flib%2Fpostgresql%2F9.6%2Fmain%20%2Fvar%2Flog%2Fpostgresql%2Fpostgresql-9.6-main.log” message=”” highlight=”” provider=”manual”/]

 

Si vous n’avez pas fait la mise à jour de votre cluster après la mise à jour de wheezy vers jessie. Vous devriez voir un truc dans ce style la.

[pastacode lang=”bash” manual=”Ver%20Cluster%20Port%20Status%20Owner%20Data%20directory%20Log%20file%0A9.1%20main%20%20%20%205432%20online%20postgres%20%2Fvar%2Flib%2Fpostgresql%2F9.1%2Fmain%20%2Fvar%2Flog%2Fpostgresql%2Fpostgresql-9.1-main.log%0A9.4%20main%20%20%20%205433%20online%20postgres%20%2Fvar%2Flib%2Fpostgresql%2F9.4%2Fmain%20%2Fvar%2Flog%2Fpostgresql%2Fpostgresql-9.4-main.log%0A9.6%20main%20%20%20%205434%20online%20postgres%20%2Fvar%2Flib%2Fpostgresql%2F9.6%2Fmain%20%2Fvar%2Flog%2Fpostgresql%2Fpostgresql-9.6-main.log” message=”” highlight=”” provider=”manual”/]

Le port par défaut de postgresql est le 5432.  Le cluster ayant 5432 comme port est votre cluster courant.

Normalement Debian installe la nouvelle version de postgresql à la mise à jour de version et il crée un nouveau cluster dans la foulée.
Si ce n’est pas le cas, vous pouvez l’installer par cette ligne:

# apt-get install postgresql-9.6

Supprimer le cluster postgresql 9.6 créé

Installer une nouvelle version de postgresql sur debian crée un cluster principal (main) qui empêche la commande pg_upgradecluster de fonctionner.

Il faut donc supprimer cette instance dans le but d’obtenir une base de données propre.
Faite très attention, ne vous tromper pas dans la commande.
Ne supprimer surtout pas votre vieille base de données. C’est elle qui contient toutes vos données. 🙂

Nous détruisons la nouvelle; la vide.

# pg_dropcluster --stop 9.6 main

Mettre Ă  jour la vieille base vers le nouveau format

La syntaxe est la suivante:

# pg_upgradecluster -v NouvelleVersion AncienneVersion main

La fonction va créer un nouvelle base main dans le format NouvelleVersion.
Pour ensuite, copier les données de AncienneVersion vers NouvelleVersion.
Cela prendra un peu de temps.

[pastacode lang=”bash” manual=”%23%20pg_upgradecluster%20-v%209.6%209.4%20main%0AStopping%20old%20cluster…%0ANotice%3A%20extra%20pg_ctl%2Fpostgres%20options%20given%2C%20bypassing%20systemctl%20for%20stop%20operation%0ADisabling%20connections%20to%20the%20old%20cluster%20during%20upgrade…%0ARestarting%20old%20cluster%20with%20restricted%20connections…%0ARedirecting%20start%20request%20to%20systemctl%0ACreating%20new%20cluster%209.6%2Fmain%20…%0Aconfig%20%2Fetc%2Fpostgresql%2F9.6%2Fmain%0Adata%20%2Fvar%2Flib%2Fpostgresql%2F9.6%2Fmain%0Alocale%20en_US.UTF-8%0Asocket%20%2Fvar%2Frun%2Fpostgresql%0Aport%205434%0ADisabling%20connections%20to%20the%20new%20cluster%20during%20upgrade…%0ARedirecting%20start%20request%20to%20systemctl%0ARoles%2C%20databases%2C%20schemas%2C%20ACLs…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20pbf…%0AAnalyzing%20database%20pbf…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20template1…%0AAnalyzing%20database%20template1…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20livres…%0AAnalyzing%20database%20livres…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20cmdline…%0AAnalyzing%20database%20cmdline…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20systemtest…%0AAnalyzing%20database%20systemtest…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20creatures…%0AAnalyzing%20database%20creatures…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20articles…%0AAnalyzing%20database%20articles…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20postgres…%0AAnalyzing%20database%20postgres…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20campagnes…%0AAnalyzing%20database%20campagnes…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20animations…%0AAnalyzing%20database%20animations…%0AFixing%20hardcoded%20library%20paths%20for%20stored%20procedures…%0AUpgrading%20database%20asso_member…%0AAnalyzing%20database%20asso_member…%0ARe-enabling%20connections%20to%20the%20old%20cluster…%0ARe-enabling%20connections%20to%20the%20new%20cluster…%0ACopying%20old%20configuration%20files…%0ACopying%20old%20start.conf…%0ACopying%20old%20pg_ctl.conf…%0ACopying%20old%20server.crt…%0ACopying%20old%20server.key…%0AStopping%20target%20cluster…%0ARedirecting%20stop%20request%20to%20systemctl%0AStopping%20old%20cluster…%0ARedirecting%20stop%20request%20to%20systemctl%0ADisabling%20automatic%20startup%20of%20old%20cluster…%0AConfiguring%20old%20cluster%20to%20use%20a%20different%20port%20(5434)…%0AStarting%20target%20cluster%20on%20the%20original%20port…%0ARedirecting%20start%20request%20to%20systemctl%0ASuccess.%20Please%20check%20that%20the%20upgraded%20cluster%20works.%20If%20it%20does%2C%0Ayou%20can%20remove%20the%20old%20cluster%20with%0A%0Apg_dropcluster%209.4%20main” message=”” highlight=”” provider=”manual”/]

Supprimer l’ancienne version

Si la copie s’est bien passée. Vous pouvez supprimer l’ancienne version. Elle est maintenant entièrement copié dans le cluster main 9.6.

# pg_dropcluster 9.4 main

Mettre en ligne ses parties !

Je suis venu vous conter une histoire. Cette histoire est issue d’un dĂ©sir coupable de frimer un peu.
Non, je n’ai pas rĂ©ussi Ă  faire quelque chose d’exceptionnel ou d’impossible comme inventer une ampoule qui dure 10 fois plus longtemps que toutes les autres.
Je souhaite juste montrer comment GNU/Linux, le système D et la philosophie OpenSource/libre/DIY peuvent permettre d’automatiser un processus de production vidĂ©o.

Contexte:

Je suis dĂ©veloppeur de Rolisteam. J’avais besoin de faire un peu la promotion du logiciel. De plus, en tant que maĂ®tre de jeu, j’avais envie de garder une trace de ma campagne, histoire
d’en faire profiter d’autres gens.
L’enregistrement des parties en vidĂ©o et audio nous a semblĂ© la meilleure solution pour atteindre ces objectifs.

Le choix des outils:

Après une phase de recherche, mon choix s’est porté sur:

SimpleScreenRecorder (SSR)

Il est pratique, simple et la qualitĂ© vidĂ©o est plutĂ´t bonne. Le seul problème c’est l’enregistrement audio. Impossible d’enregistrer en mĂŞme temps mon micro et
les voix de mes camarades Ă  moins de configurer pulseaudio ou l’usage de Jack. Je n’avais pas envie de changer toute la configuration son de ma bĂ©cane. C’est peut-ĂŞtre facile Ă  faire mais j’avais pas envie de me lancer la dedans. De plus, la qualitĂ© audio de SSR est moins bonne que Teamspeak (je trouve en tout cas).

Teamspeak:

Ok, ce n’est pas libre mais pour des raisons de qualitĂ© et d’habitude des joueurs, on est restĂ© sur cette solution pour l’audio.
Les fruits de l’enregistrement

Grâce Ă  ces outils, j’ai rĂ©alisĂ© les enregistrements.
J’avais donc un fichier son et une vidĂ©o par partie. Dans un premier temps, j’ai fusionnĂ© les deux fichiers avec ffmpeg pour obtenir la vidĂ©o de la partie (avec le son).

ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -strict experimental output.mp4

Améliorer l’expérience visuelle

Souhaitant offrir une meilleure expĂ©rience pour les Ă©ventuels spectateurs, j’ai codĂ© un plugin Ă  teamspeak qui envoie sur dbus le statut de la voix de chaque joueur.

J’ai crĂ©e une application qui Ă©coute ces messages dbus, quand le joueur parle, le portrait de son personnage s’affiche en couleur. Quand il est silencieux le portrait du personnage s’affiche en niveau de gris. Il m’a fallu plusieurs tests et Ă©tapes pour arriver Ă  ce fonctionnel final.
Les joueurs ne voulant pas montrer leur visage par webcam. Cela semblait la meilleur solution de suivre facilement les conversations.

Vous trouverez le code ici: https://github.com/obiwankennedy/GameVisualisationHelper/tree/cops/display

Cette application a apportĂ© une contrainte supplĂ©mentaire sur les vidĂ©os. Le son et l’image doivent ĂŞtre synchronisĂ©s avec prĂ©cision. Ce n’était pas le cas avant car les Ă©lĂ©ments de l’écran pouvait ĂŞtre en avance ou en retard par rapport Ă  la voix, il n’y avait aucun repère visuel pour le remarquer.

Caler le son et l’image

J’ai d’abord pensĂ© merger les deux fichiers dans un Ă©diteur vidĂ©o.
J’ai essayĂ© PitiVi, OpenShot et Kdenlive. Les deux premiers agonisent dans d’atroces souffrances après le chargement de fichiers supĂ©rieurs Ă  3h. Kdenlive s’en sort mieux. Il n’agonise qu’une fois sur deux. J’ai installĂ© les versions de ma distribution et j’ai fait de report de bug mais je pouvais pas attendre la rĂ©solution des problèmes.

Dans ce contexte, trouver le bon timing pour synchroniser l’audio sur l’image, c’est compliquĂ©. Le drag and drop de fichier de 3h fait assez mal aux logiciels d’Ă©dition vidĂ©o. Sans parler de la prĂ©cision pour les dĂ©placements. Bref, pas pratique.

Je me suis dit “c’est idiot, il faudrait synchroniser le dĂ©but de l’enregistrement de l’audio et la vidĂ©o”.
Je n’ai pas le code source de Teamspeak mais il est possible de crĂ©er un plugin (ou de modifier celui que j’ai créé) et j’ai le code source de SimpleScreenRecorder (SSR).

J’ai donc dĂ©cidĂ© d’exposer l’API de SSR sur DBUS et mon plugin teamspeak envoie des commandes dbus. Vive Dbus !
En gros, j’ai Ă©tudiĂ© le code de SSR pour identifier la fonction qui dĂ©marre l’enregistrement. J’ai créé la petite tambouille pour l’exposer (ainsi que la fonction pour mettre l’enregistrement en pause) sur dbus.

Si vous voulez voir comment faire cela: http://renaudguezennec.eu/index.php/2011/03/10/introduction-a-dbus-avec-qt4/

Pour le coup, après quelques essais et des modifications sur l’ensemble des participants Ă  l’affaire. J’ai une solution qui tourne bien. Je peux utiliser ffmpeg pour fusionner mes fichiers vidĂ©os avec le son et cela correspond parfaitement.

ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -strict experimental output.mp4

Une étape d’accomplie

J’ai fait un pull request Ă  l’auteur de SSR. Mon but Ă©tait de montrer comment faire car l’auteur de SSR n’est pas formĂ© Ă  Dbus. Il est très intĂ©ressĂ© mais clairement je n’ai pas le temps de gĂ©nĂ©raliser l’usage de Dbus dans SSR, ni lui d’ailleurs.

Ma version est accessible ici : https://github.com/obiwankennedy/ssr
Ma pull request : https://github.com/MaartenBaert/ssr/pull/399

Montage des génériques

Après ces Ă©tapes, j’ai des vidĂ©os de mes parties assez brutes. IdĂ©alement, il me reste Ă  ajouter un gĂ©nĂ©rique de dĂ©but et de fin.

Pour le faire, j’ai fait un programme en QML avec deux animations qui se courent après. C’est pas jolie mais cela fait le job. Le gĂ©nĂ©rique dure ~10 secondes, je l’ai enregistrĂ© avec SSR aussi.
J’ai utilisĂ© Kdenlive pour caler une musique libre dessus.

A la fin de cette Ă©tape, j’ai ma petite vidĂ©o de gĂ©nĂ©rique de dĂ©but, idem pour le gĂ©nĂ©rique de fin et mes Ă©pisodes (plus de 75).
Vous l’avez compris, l’Ă©tape ici est de crĂ©er des vidĂ©os contenant les gĂ©nĂ©riques.
J’ai cherchĂ© un peu dans ffmpeg pour arriver Ă  cela. C’est une simple fonction de concatĂ©nation des vidĂ©os.
J’ai Ă©crit ma petite commande, ça marche.

[pastacode lang=”bash” manual=”cd%20%2Fracine%2Fdes%2Fvideos%2F%0AOPENING%3D%2Fchemin%2Fvers%2Fgenerique%2Fdebut.mp4%0AENDING%3D%2Fchemin%2Fvers%2Fgenerique%2Ffin.mp4%0Avideo%3D%2Fchemin%2Fvers%2Fvideos.mp4%0ALIST_FILE%3D%2Ftmp%2Fmylist.txt%0AvideoExtLess%3D%5C%60echo%20%24video%20%7C%20awk%20-F%20′.’%20’%7Bprint%20%241%7D’%5C%60%0Aecho%20%22file%20’%24OPENING’%22%20%3E%20%24LIST_FILE%0Aecho%20%22file%20’%24video’%22%20%3E%3E%20%24LIST_FILE%0Aecho%20%22file%20’%24ENDING’%22%20%3E%3E%20%24LIST_FILE%0Affmpeg%20-safe%200%20-f%20concat%20-i%20%2Ftmp%2Fmylist.txt%20%24%7BvideoExtLess%7D_avec_generiques.mp4″ message=”Concat avec ffmpeg” highlight=”” provider=”manual”/]

Du moins, je croyais que cela marchait. En vérité, cela cassait la synchronisation son/image. Ce fut très embêtant.
Je retourne Ă  la case dĂ©part “logiciel de montage vidĂ©o” (Kdenlive). Il y a un peu moins de manipulation prĂ©cise Ă  faire. Je colle les trois fichiers: gĂ©nĂ©rique de dĂ©but, l’épisode, gĂ©nĂ©rique de fin et c’est parti.
Ça a fonctionné un temps.

Extraction du son et traitement

Une fois la vidĂ©o complète avec les gĂ©nĂ©riques. J’en extrait le son pour la diffusion en podcast, vraiment facile avec ffmpeg.

ffmpeg -i videos.mp4 -codec:a libmp3lame -qscale:a 2 output.mp3

Avant de sortir l’Ă©pisode en podcast, je le re-travaille un peu avec audacity pour supprimer les silences et pour normaliser le son (avec le filtre compresseur du logiciel).
Cela raccourcit l’épisode d’environ 30 mins et le son est bien meilleur.

Traitement sur la vidéo

Je souhaite effectuer les mĂŞmes traitements sur les vidĂ©os. J’ai bien luttĂ© pour trouver une solution.

Normalisation

Pour la normalisation, j’ai trouvĂ© un petit script python: ffmpeg-normalize.

ffmpeg-normalize -vu -p normalized-episode40.mp4 Episode_40.mp4

Supprimer les moments inutiles

Couper la vidĂ©o quand il y a du silence fut bien plus complexe. Aucun logiciel de montage vidĂ©o n’offre de filtre pour cela (je n’ai pas trouvĂ© en tout cas) et j’allais pas le faire Ă  la main. Cela m’aurait pris trop de temps.

J’ai donc cherchĂ© Ă  droite Ă  gauche et c’est Ryzz (Merci Ă  lui) sur Linuxfr.org qui m’a envoyĂ© vers une bonne piste: MoviePy

Un module python pour faire de l’Ă©dition vidĂ©o. Il y a mĂŞme un exemple utilisant MoviePy pour crĂ©er le rĂ©sumĂ© d’un match de foot grâce au son des supporters.

J’ai donc créé un script python pour couper les silences. J’en ai profitĂ© Ă©galement ajouter les gĂ©nĂ©riques avec MoviePy.
Quand mon script fut prĂŞt j’avais dĂ©jĂ  sorti 20 Ă©pisodes. J’en avais donc 40 en stock Ă  refaire. J’ai donc lancer le script sur les 40 restant. Après 3 jours d’exĂ©cution. J’avais
tous mes épisodes prêts.

Si vous souhaitez voir le code du script et des explications techniques:
http://renaudguezennec.eu/index.php/2017/03/03/montage-video-en-python/

La réduction des silences est moins efficace qu’audacity mais il y a un gain indéniable.

VoilĂ , le degrĂ©s maximum que j’ai atteins dans l’automatisation.

Aller encore plus loin

Dans tout ce processus, il me manque encore des choses pour vraiment automatiser toute la chaîne.

Le premier point, c’est audacity. Il n’est pas possible d’utiliser audacity en ligne de commande. Je me suis un peu renseignĂ©, il y a eu des tentatives mais c’est très complexe Ă  mettre en place.
J’ai regardĂ© un peu le code, espĂ©rant trouver un moyen d’appliquer les deux filtres dont j’ai besoin. Le code est peu lisible avec des define partout pour diffĂ©rencier les OS. Bref, un cauchemar Ă  maintenir. Du coup, j’hĂ©site Ă  mis mettre vraiment ou rester en Ă©dition manuelle.

Ensuite, il me reste à automatiser la partie “mise en ligne” sur youtube et sur le wordpress.
Je suis certains que c’est possible mais pour l’instant, je n’ai pas pris le temps de le faire.
Si vous voulez voir le résultat:

Le wordpress pour écouter les épisodes: http://blog.rolisteam.org/
La playlist youtube des épisodes : https://www.youtube.com/playlist?list=PLBSt0cCTFfS5fi3v1LtB9sfeA8opY-Ge1
Les premiers n’ont pas bénéficié de tous les outils. Il y a clairement une marque de progression dans les épisodes jusqu’à l’épisode 20 environ.

Montage Vidéo en python

Mon besoin

Dans le cadre de la promotion de Rolisteam, je diffuse en ligne des enregistrements de parties.
Ces enregistrements nécessitent différents traitement afin d’être rendu plus audibles et intéressants.
Les taches à réaliser sont les suivantes:

1 – Associer la piste audio et la piste vidĂ©o
2 – Ajouter le gĂ©nĂ©rique de dĂ©but et de fin.
3 – Couper la vidĂ©o en fonction des silences
4 – AmĂ©liorer le son (le compresser/normaliser)

Pour réaliser la tache 1, une simple commande ffmpeg suffit. J’ai facilement créé un script bash pour l’automatiser.
La tâche 2 peut également être réalisée par ffmpeg avec l’option concat mais cela ne s’est pas passé comme prévu.
La solution de replie fut kdenlive, un logiciel de montage vidéo sur linux (Un des rares qui ne plante pas tout le temps quand on lui donne à monter une vidéo de trois heures).

L’étape 3 fut bien plus complexe à réaliser. Il n’y a aucun outil clé en main pour faire cela dans ffmpeg ou kdenlive. C’est très probablement faisable avec ces outils mais je n’ai pas trouvé comment. Je ne me voyais pas couper les moments de silence à la main.
Je commençais à désespérer quand Ryzz sur Linuxfr.org a évoqué le package MoviePy. Un module de manipulation vidéo pour python avec un exemple d’emploi proche de mon objectif.

Créer un résumé automatique d’un match de foot: http://zulko.github.io/blog/2014/07/04/automatic-soccer-highlights-compilations-with-python/
La doc de l’API: http://zulko.github.io/moviepy/index.html

Le code python: MoviePy

Voila un outil pour manipuler le son et la vidéo en codant avec python. Outil parfait pour réaliser les actions 2 et 3 de façon automatique.
Mes fichiers d’entrées sont biens rangés dans des dossiers, cela rend l’automatisation plus facile.

Pour la dernier Ă©tape, j’ai trouvĂ© un petit script python “ffmpeg-normalize” qui fait cela. Ce n’est clairement pas aussi puissant que les filtres d’audacity mais cela suffit.

Première étape, Parcourir les dossiers, trouver fichier associant la vidéo et le son pour y ajouter les génériques.

[pastacode lang=”python” manual=”for%20subfolder%20in%20sorted(os.listdir(rootFolder))%3A%0Aif(%22_done%22%20not%20in%20subfolder)%3A%0Afor%20subfile%20in%20os.listdir(os.path.join(rootFolder%2Csubfolder))%3A%0Aif((%22mp4%22%20in%20subfile)and(%22Partie%22%20in%20subfile))%3A%0Alink%20%3D%20os.path.join(rootFolder%2Csubfolder)%0Avideo%20%3D%20os.path.join(link%2Csubfile)%0Adest%20%3D%20subfile.replace(%22.mp4%22%2C%22_ending.mp4%22%2C1)%0Adestination%20%3D%20os.path.join(link%2Cdest)%0Avideoclip%20%3D%20VideoFileClip(video)%0A%23concatenation%20of%20opening%2C%20video%20and%20ending%0Afinalclip%20%3D%20concatenate_videoclips(%5Bopening%2Cvideoclip%2Cending%5D)%0Afinalclip.write_videofile(destination%2Cfps%3D25)” message=”Parcours des dossiers” highlight=”” provider=”manual”/]

Je ne fais que reconstruire le chemin pour arriver jusqu’à l’épisode.

Ensuite, l’autre partie intéressante (grandement inspiré de l’exemple sur le foot), est de découper le film en échantillon d’une seconde. Le volume de chaque échantillon est calculé.
Il faut stocké le volume de chaque échantillon.

[pastacode lang=”python” manual=”%23split%20resulting%20video%20in%20audio%20subclip%0Aclip%20%3D%20VideoFileClip(destination)%0Acut%20%3D%20lambda%20i%3A%20clip.audio.subclip(i%2Ci%2B1).to_soundarray(fps%3D22000)%0Avolume%20%3D%20lambda%20array%3A%20np.sqrt(((1.0*array)**2).mean())%0Avolumes%20%3D%20%5Bvolume(cut(i))%20for%20i%20in%20range(0%2Cint(clip.audio.duration-2))%5D%0Afinal_times%3D%20%5B%5D” message=”Les volumes” highlight=”” provider=”manual”/]

Le dernière étape consiste à regrouper les périodes de temps qui doivent être sauvegardé. Dans un tableau à deux éléments, je conserve le début et la fin de chaque période à conserver.

[pastacode lang=”python” manual=”i%20%3D%201%0Aduo%20%3D%20%5B%5D%0Astart%20%3D%20-1%0Aend%20%3D%200%0AsumVideo%20%3D%200%0A%23identify%20all%20part%20with%20sounds.%20What%20we%20keep.%0Afor%20vol%20in%20volumes%3A%0Aif((%20vol%20%3D%3D%200.0%20)and%20(start!%3D-1))%3A%0Aend%20%3D%20i-1%0Aduo%20%3D%20%5Bstart%2Cend%5D%0Afinal_times.append(duo)%0AsumVideo%20%2B%3D%20(end-start)%0Astart%20%3D%20-1%0Aif((start%20%3D%3D%20-1)and%20(vol%3E0.0))%3A%0Astart%3Di%0Ai%3Di%2B1″ message=”SĂ©paration des duos” highlight=”” provider=”manual”/]

Quand l’ensemble des duos sont identifiés, il suffit de les concatener dans le fichier de sortie.

[pastacode lang=”python” manual=”finalpath%20%3D%20destination.replace(%22_ending%22%2C%22_cutted%22)%0Aprint%20finalpath%0Aprint%20final_times%0A%23concatenate%20all%20kept%20parts.%0Afinal%20%3D%20concatenate_videoclips(%5Bclip.subclip(t%5B0%5D%2Ct%5B1%5D)%0Afor%20t%20in%20final_times%5D)%0A%23write%20the%20file%0Afinal.write_videofile(finalpath%2Cfps%3D25)” message=”Sauvegarde des Ă©chantillons” highlight=”” provider=”manual”/]

Code complet: http://www.renaudguezennec.eu/file/cutVideos.py