DédéAmp arrive ! – part 2

La fois dernière nous avons tout juste vu de quel bois est fait DédéAmp. Aujourd’hui, il est temps d’avancer un peu plus dans le code…

Les objets

Avant de créer un objet, il faut le définir. C’est le cas pour notre objet formulaire (et ses boutons), les images, le média player, et même l’objet custom dont je vous ai parlé dans l’article précédent. Celui-ci étant un peu à part, nous verrons sa création plus loin. Pour les autres objets, ceux-ci étant fournis par des composants .NET (des assemblies) ou taglib-sharp.dll, il faut d’abord charger tout ce petit monde :

#region Chargement windows.forms & taglib-sharp.dll
    [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    #[System.Windows.Forms.Application]::EnableVisualStyles()
    Add-Type -AssemblyName PresentationCore 
    # Pensez à récupérer taglib-sharp.dll depuis ce site https://download.banshee.fm/taglib-sharp/2.1.0.0/taglib-sharp-2.1.0.0-windows.zip
    # et à la débloquer si nécessaire (dans les propriétés avancées de la DLL)
    # à copier dans le même répertoire que DédéAmp
    #[Reflection.Assembly]::LoadFrom("\\$path\taglib-sharp.dll") | Out-null # Windows 7
    Add-Type -path "$path\taglib-sharp.dll" | Out-null # Windows 10
#endregion
  • System.Windows.Forms, c’est comme son nom l’indique, le formulaire et tout ce qui va avec : les labels, textboxes, boutons et j’en passe.
  • System.Drawing vous nous servir pour tout ce qui est images (et notamment les icônes des boutons, la pochette de l’album…)
  • PresentationCore (qui est chargé par la cmdlet Add-Type) est nécessaire pour la partie média player
  • Et enfin, nous chargeons taglib-sharp.dll

Pour une raison qui m’échappe encore, selon la version de Windows (7 ou 10), j’ai été obligé de charger taglib-sharp.dll différemment. Ainsi sous Windows 7, et sur ma machine (ce sera peut-être différent sur la vôtre), je dois utiliser la ligne

    [Reflection.Assembly]::LoadFrom("\\$path\taglib-sharp.dll") | Out-null # Windows 7

…alors que sous Windows 10, c’est :

    Add-Type -path "$path\taglib-sharp.dll" | Out-null # Windows 10

Va comprendre, Charles. Peu importe la méthode, le principal est qu’elle finisse par se charger. Notez l’utilisation de la variable $path qui pointe vers le répertoire où se trouve le script (et donc la DLL, puisque vous l’avez copié dans ce même répertoire, n’est-ce pas ?).

Les fonctions

Dans le script, après la définition de mes variables et le chargement de mes assemblies, viennent les fonctions. Le nom est assez évocateur, mais pour être bien précis :

  • La fonction Play lance la lecture de la musique. C’est très réducteur comme description, car en réalité elle fait beaucoup plus de choses que ça, mais nous la détaillerons plus tard.
  • La fonction Next permet de changer de morceau en avançant dans la playlist.
  • La fonction Prev permet de changer de morceau en reculant dans la playlist. Là aussi, nous décortiquerons le code plus tard.
  • La fonction Updatepos permet de mettre à jour le compteur de temps (et donc la position dans le morceau), et ce en quasi-temps réel.
  • La fonction Scandir permet de scanner le répertoire à la rechercher de MP3 à jouer, mais surtout crée notre playlist : celle-ci est une liste d’objets dans lequel je stocke le nom du fichier (pour pouvoir le rejouer, c’est pratique), mais aussi tout un tas d’informations récupérées depuis les tags ID3 (dont l’artiste, le titre, l’album, le numéro de la piste, la durée, et même la pochette de l’album).
  • La fonction Selectdir permet d’afficher le dialogue du sélecteur de répertoire (un composant de Windows.forms), ce qui laisse à l’utilisateur la possibilité d’en choisir un (je suis trop sympa moi des fois).
  • Enfin, la fonction Mute permet de couper ou rallumer le son (en gros, de mettre le volume à 0 ou à 1, selon l’état de ce dernier : s’il était à 0, je le passe à 1, et inversement. C’est tout simple).

Images et icônes

Plutôt que de charger les images/icônes depuis le disque, j’ai préféré les intégrer au script. Pour cela, j’ai utilisé le script ImageToBase64.ps1 qui permet de sélectionner un fichier (dans mon cas, une image au format PNG, mais il accepte et convertit tout et n’importe quoi) et de le transformer dans un fichier texte rempli de trucs bizarres…

ImageToBase64.ps1 dans toute sa splendeur

Le principe consiste à transformer une suite d’octets (le contenu du fichier image) en une chaîne de caractères limitée à un alphabet de 64 signes (et un 65ème, le signe “=”). L’intérêt de la chose est de permettre la transmission de données binaires en utilisant un alphabet compatibles avec tous les systèmes du monde (si ça vous rappelle l’ASCII, vous avez raison). Comme vous le savez certainement, un octet (de 8 bits) permet d’avoir 256 valeurs différentes (de 0 à 255, si cet octet n’est pas signé, mais ne rendons pas les choses inutilement complexes). Or le souci, c’est que le standard ASCII n’est plus un standard après le 127ème caractère… Le A signifiant “American”, nos amis d’outre-Atlantique ne se sont pas vraiment préoccupé des accents qu’on trouve en français et encore moins des autres langues… Dans l’ASCII d’origine, il n’y ainsi que les caractères latins (A, B, C…, a, b, c…), les chiffres arabes (0 à 9) et quelques signes communs de ponctuation et d’arithmétique. Le reste ? Bah… chacun fait ce qu’il veut. Et c’est un peu le souci. D’où le base64 : on ne retient qu’un sous-ensemble de caractères (les plus communs), et pour on fait tenir nos 256 valeurs d’un octet en utilisant seulement 64 signes différents… Mais comment faire tenir 8 bits en seulement 6 bits ? Et bien la seule solution, c’est d’augmenter la taille de notre chaîne d’origine… En effet, un fichier encodé en base64 donnera une chaîne dont la taille est supérieure d’un tiers à celle d’origine… Oui mais au moins ce sera transmis proprement. La preuve : les pièces jointes dont vous saturez vos mails sont transmises d’un serveur à votre client de messagerie en texte brut… et encodées en base64 ! Pensez-y : une pièce jointe d’un mégaoctet alourdi le message de 300 Ko supplémentaires ! Ce n’est pas négligeable… Si vous voulez en savoir plus sur la base64, n’hésitez pas à consulter l’article correspondant sur Wikipédia, il vous expliquera tout ça bien mieux que moi (même si je comprends parfaitement le principe, la restitution de l’explication, ce n’est pas forcément mon fort…).

Revenons-en à DédéAmp. Je prends donc ImageToBase64.ps1, je clique sur le bouton Browse pour aller chercher une image, je clique sur le bouton Convert et hop, j’ai un fichier texte dont le contenu ressemble à ceci :

Ah bah super, c’est quoi tout ce charabia ?

C’est du base64. Et vous pouvez voir que c’est lisible par un humain : tout est dans l’alphabet, pas de caractères bizarroïdes. La suite consiste à sélectionner tout ce texte (du “i” du début au “=” final), et à le coller dans une variable chaîne pour pouvoir l’utiliser dans Powershell. Celle-ci doit absolument être délimitée, et le texte doit être entre @” et “@ :

#region Images & icônes
# Ici sont stockées en base64 les images et icônes, ce qui évite de les charger depuis des fichiers
[string]$about=@"
Coller ici le bloc de texte en base 64
"@
#endregion

Cela alourdit le script certes, mais en attendant mes images sont directement intégrées, sans avoir besoin de les charger. Ensuite, j’utilise ma fonction base64toimage pour en refaire quelque chose de compréhensible (en gros, je retransforme la chaîne encodée en base64 en une image binaire) pour le formulaire Windows.form (qui ne comprend rien à la base64) :

$aboutimage=base64toimage($about)

N’oubliez pas : la base64 ne sert qu’au stockage au sein du script de mon image. Je ne pourrais pas mettre directement le contenu binaire (sur 8 bits) dans le script, cela sera une catastrophe (à cause de caractères illisibles, de codes de contrôle, et j’en passe). Donc je stocke en base64, puis je retransforme en image avec ma fonction base64toimage que voici :

function base64toimage($base64image)
{
    $bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
    $bitmap.BeginInit()
    $bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64image)
    $bitmap.EndInit()
    $bitmap.Freeze()
    $image = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($bitmap.StreamSource)
    $image
}

Lorsque j’appelle ma fonction, je lui transmet la chaîne base64 de mon image (qu’elle reçoit dans la variable $base64image). Elle crée ensuite un objet de type System.Windows.Media.Imaging.BitmapImage qui attend un flux d’octet (ou stream) lu depuis la mémoire. Enfin, elle convertit ce stream en image bitmap (le contenu exact de mon fichier PNG d’origine) et la renvoie à l’appelant (la variable $image). La méthode Freeze() est importante, car elle évite les fuites d’octet (et donc d’écrire en RAM des données qui n’auraient rien à voir avec mon image et pourraient potentiellement poser problème). Ça a l’air compliqué ? En réalité c’est assez simple, et c’est Powershell qui se tape tout le boulot, alors bon…

Ainsi, si je reprend ma ligne :

$aboutimage=base64toimage($about)

…la variable $aboutimage va contenir l’image décodée que j’avais stocké en base64 (dans la chaîne $about). Je m’en servirai plus loin lors de la création du formulaire, pour définir la propriété .Image (ou .BackgroundImage, selon le type d’objet) d’un bouton par exemple.

$button_info.Image=$aboutimage

Si stocker des images en base64 vous ennuie, vous pouvez très bien vous en passer et ne rien stocker dans le script. Du coup, pour mettre une icône dans un bouton, vous pourrez vous contenter de ceci :

$button_play.Image=[System.Drawing.Image]::FromFile("$path\play.png")

Il faut évidemment que play.png existe, ait la bonne résolution (genre la photo du petit dernier prise au Canon 5Dmk4 en résolution maxi, vous oubliez, hein), et qu’il soit dans le même répertoire que le script (d’où l’utilisation de la variable $path)…

Création des objets

La section suivante du code crée tout simplement deux objets :

#region Création des objets
    $MediaPlayer = New-Object System.Windows.Media.MediaPlayer
    $playlist= New-Object System.Collections.ArrayList
#endregion

Comme leurs noms respectifs l’indiquent, $MediaPlayer est l’objet qui va permettre de rejouer le MP3 (en réalité, il appelle Windows Media Player et lui indique le MP3 à jouer), et $playlist est comme vous pouvez le voir une ArrayList, autrement dit un tableau mais un peu particulier, car il contiendra une liste d’objet : ceux que nous allons créé pour chaque fichier MP3 trouvé dans le répertoire sélectionné… Nous y reviendrons en détails.

La fois prochaine, nous nous attaquons à un gros morceau : la création du formulaire, qui est un peu l’âme de DédéAmp.

Stay tuned et tchüss !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.