Il ne nous reste plus qu’à voir une dernière fonction et quelques bricoles avant le grand moment : celui où vous allez pouvoir lancer DédéAmp et écouter de la musique avec !
Old timer
Comme je l’ai dit dans l’article précédent, afin de pouvoir mettre à jour la textbox contenant la position dans le morceau, il nous faut un timer, c’est-à-dire un événement créé de toutes pièces qui se répétera périodiquement (dans notre cas, toutes les secondes), et que nous intercepterons. Voilà comment le définir :
# Timer pour mettre à jour le formulaire pendant la lecture $global:timer = New-Object System.Windows.Forms.Timer $global:timer.Interval = 1000 # en ms, donc 1000 = 1s $global:timer.Enabled = $true $global:timer.add_Tick({updatepos})
Et oui, notre timer est une fois de plus un objet, avec ses méthodes et propriétés. En l’occurrence, il s’agit d’un objet de type Windows.Forms.Timer (ça alors) pour lequel je mets quelques propriétés :
- Interval : la fréquence en millisecondes. Pour une seconde, j’ai donc mis la valeur à 1000. Dans notre cas, en dessous ça ne sert à rien, et au-dessus… non plus. L’idée est d’avoir un compteur de position en (quasi) temps réel, tant qu’à faire…
- Enabled : permet d’activer (à $true) ou de désactiver (à $false) le timer.
- Enfin, s’agissant d’un objet, on utilise la méthode add_Tick() pour lui dire gentiment d’exécuter la fonction updatepos toutes les secondes.
Attention, avec Enabled nous n’avons fait qu’activer le timer. Nous ne l’avons pas démarré ! Rappelez-vous : dans la fonction play, il y a cette ligne qui sert à démarrer le timer :
$global:timer.start()
Évidemment, il faut pouvoir aussi l’arrêter. Et pour cela, l’objet timer possède la méthode Stop(). Celle-ci n’est pas appelé par une fonction stop (il n’y en a pas), mais directement lorsqu’on clique sur le bouton Stop de DédéAmp :
$button_stop.Add_Click( { $textbox8.Text="00:00:00" $button_play.Image=[System.Drawing.Image]::FromFile("$path\play.png") $textbox7.Text="En attente" $global:status=0 $progress.Value = 0 $form.Refresh() $global:timer.stop() $MediaPlayer.Stop() $MediaPlayer.Close() } )
Notez que je ne fais pas qu’arrêter le timer : il faut aussi arrêter la lecture ! Pour cela, j’appelle successivement les méthode Stop() et Close() de mon objet $MediaPlayer. En plus de mettre deux-trois trucs à jour dans le formulaire.
La suite des événements
Il reste encore deux événements à gérer :
- Passer au morceau suivant automatiquement lorsque le premier morceau est terminé (on est arrivé au bout)
- Que faire lorsque l’utilisateur quitte DédéAmp (autrement dit, quand il ferme le formulaire en cliquant sur la croix en haut à droite de la fenêtre)
Pour le passage d’un morceau à l’autre, je vais aussi gérer un événement, mais sans créer d’objet.
# Eventhandler pour passer au morceau suivant lorsqu'un se termine Register-ObjectEvent -InputObject $mediaplayer -SourceIdentifier media -EventName MediaEnded -Action {next} | Out-Null
J’utilise donc la cmdlet Register-ObjectEvent, en lui demandant gentiment de surveiller mon objet $MediaPlayer. Dans celui-ci, il y a une source d’événements nommée media, et l’évènement qui m’intéresse est MediaEnded (la fin de mon morceau). Enfin, je lui précise l’action à mener, en l’occurrence appeler la fonction next. Enfin, j’envoie le résultat de la cmdlet vers Out-Null non seulement parce que je m’en fous (ça marche toujours, enfin sauf si je me plante dans les paramètres), mais surtout pour éviter que Powershell n’écrive des trucs dans la console.
Pour ce qui est de la fermeture du formulaire, c’est directement sur cet objet que je vais intercepter l’événement correspondant, car il faut toujours quitter un programme proprement (ça évite que le morceau continue à jouer, et que mon gestionnaire d’événement soit toujours actif…) :
$form.add_FormClosed( { $global:timer.stop() $MediaPlayer.Stop() $MediaPlayer.Close() Unregister-Event -SourceIdentifier media } )
On arrête donc le timer et le morceau, et on « désenregistre » le gestionnaire d’événement qu’on a créé quelque lignes au-dessus. Après hop, le ménage étant fait, notre formulaire se ferme tout seul : il ne faut surtout pas appeler la méthode Close() de $form, sans quoi on rentrerait dans une boucle infinie ! En effet, le formulaire est déjà en cours de fermeture quand on intercepte l’événement FormClosed, alors si ensuite on fait un $form.Close(), l’événement FormClosed sera regénéré, et on réexécutera sans cesse le code ci-dessus !
La fonction updatepos
La voilà cette dernière fonction. Elle ressemble à ça :
function updatepos { if($global:status -eq 1) { $position=[math]::Round($MediaPlayer.Position.TotalSeconds) $duration=[math]::Round($MediaPlayer.NaturalDuration.TimeSpan.TotalSeconds) $percent =[math]::Round($position/$duration*100,2) $progress.Value = $percent $pos = [timespan]::fromseconds($position) $pos.ToString("hh\:mm\:ss") $dur= [timespan]::fromseconds($duration) $dur.ToString("hh\:mm\:ss") $textbox8.Text=$pos $textbox9.Text=$dur $textbox8.refresh() $textbox9.refresh() $progress.refresh() } }
Avant de nous lancer dans le décorticage en règle, rappelez-vous ce que vous avez vu sur le formulaire : outre des textboxes et des labels, il y a une jolie barre de progression. Celle-ci est définie comme il se doit dans la région Formulaire comme ceci :
# Barre de progression $progress = New-Object System.Windows.Forms.ProgressBar $progress.Location = New-Object System.Drawing.Point(20,80) $progress.Name = 'progressBar' $progress.Size = New-Object System.Drawing.Size(380,4)
C’est un objet de type Windows.Forms.ProgressBar, et là je lui indique une position, un nom et une taille en pixel (380 de largeur, 4 de haut). J’aurai pu spécifier d’autres propriétés, mais non. La seule qui va m’intéresser n’est pas dans la définition, mais vous pouvez la voir dans la fonction updatepos : $progress.Value. L’idée d’une barre de progression est d’afficher visuellement une proportion de chose par rapport à un total de cette même chose. En langage humain, une proportion s’indique en pourcentage (et pas en cuillères à soupe, on ne fait pas de la pâtisserie, là). Pour nous simplifier la tâche et nous éviter une règle de trois, la barre de progression peut prendre des valeurs de 0 à 100, sans signe pourcent parce que c’est implicite. Et Windows se charge pour nous de calculer combien de pixels tout cela fait.
Comme je le disais, ma barre fait 380 pixels de large. Donc à 50% de ma progression, je suis censé une barre verte de 190 pixels. Très bien, mais avec mon morceau, je ne dispose que de durée, pas de pourcentage ! Et bien peu importe, mes rudiments de mathématiques niveau lycée (j’ai un bac B, pas S !) vont suffire pour me débrouiller : j’ai besoin de la durée totale du morceau et de la position dans celui-ci. Je dois donc les récupérer, grâce aux propriétés Position.TotalSeconds et NaturalDuration.TimeSpan.TotalSeconds, que j’arrondis afin de ne pas avoir de millisecondes dans le tas.
Ensuite, ma règle de trois, pour avoir une valeur en pourcentage $percent : position/durée * 100. Que j’arrondis avec la méthode Round() avec 2 chiffres après la virgule (ça fait laaaaaaargement l’affaire niveau précision. En effet, 1% de 380 pixels faisant 3,8 pixels, il me faudra des incréments d’environ 0,25% pour avoir un pixel en plus sur ma barre. Donc 2 chiffres après la virgule. Et hop.
Ensuite, je vais devoir convertir un nombre entier (ma position et ma durée sont renvoyées sous forme d’entiers : par exemple, une durée de 3mn = 180 secondes) en un format plus sympa pour les humains, en hh:mm:ss.
$pos = [timespan]::fromseconds($position) $pos.ToString("hh\:mm\:ss") $dur= [timespan]::fromseconds($duration) $dur.ToString("hh\:mm\:ss")
Je rafraîchis ensuite mes deux textboxes et ma barre de progression, et voilàààà ! C’est fini ! Comme la fonction updatepos est appelée toutes les secondes, j’ai un joli compteur et une barre de progression en temps réel, youhou !
This is the end, my friend
Et oui, c’est terminé ! DédéAmp a fini d’arriver, il est là ! A vous la joie d’écouter votre compil de chants de Noël au coin du feu ! A vous la joie aussi d’améliorer et de modifier DédéAmp comme bon vous semble ! Il vous suffit de télécharger l’archive ci-dessous, de lancer Powershell (ISE) et d’exécuter dedeamp.ps1 !
Dans cette série d’articles, j’ai expurgé les commentaires et un peu de bazar qui est resté dans le code source présent dans l’archive. La plupart des icônes ne sont pas intégrées dans le script, il reste du travail à faire, probablement un peu de débogage, mais peu importe, DédéAmp fonctionne et surtout, il est là ! Enjoy !
Remerciements
- L’ami @gootsy qui m’a (bien involontairement) donné l’idée de faire DédéAmp
- Les gens qui ont fait taglib-sharp.dll mais qui n’ont pas fourni de doc (pour certaines choses, j’ai du lire le code source en C++ avant de trouver la doc… qui est par ailleurs totalement imbitable)
- Ma maman et mon papa sans qui je ne serai pas là, obviously
- Toi, lectrice (ou lecteur) assidue de ce blog. Oui toi. Tu es unique. Sache-le. Enfin, peut-être que vous êtes deux à lire. Trois grand maximum. Mais je vous aime.
Joyeux Noël, bonne fête du solstice d’hiver, et bonne année 2017, en espérant qu’elle ne soit pas aussi pourrie que 2016, même si je ne suis pas super optimiste à ce sujet. Au moins vous pourrez écouter un peu de musique. Portez-vous bien, et tchüss !