DédéAmp : ajouter une fonction shuffle

Vous avez pu passer le réveillon de Noël à vous goinfrer tout en écoutant de la musique avec DédéAmp, et ça vous a bien plu (parce que vous étiez bourré) ? Mais peut-être avez-vous regretté que les morceaux de la playlist s’enchaînent dans l’ordre, et pas de manière aléatoire… Et bien nous allons corriger tout ça, en ajoutant cette fonctionnalité à votre lecteur de MP3 favori !

Si vous avez lu toute la série d’articles sur DédéAmp, vous devez vous souvenir que trier les morceaux dans l’ordre des pistes de l’album n’avait pas été une partie de plaisir pour moi… même si au final, tout s’est fait en trois ou quatre lignes de code. La fonctionnalité shuffle (ou lecture aléatoire) va permettre de bouleverser l’ordre établi, mais sans tout casser quand même (en gros, il s’agit d’une révolution à la sauce frondeurs du PS… hmm, je m’égare, désolé).

De quoi allons-nous avoir besoin ?

  • D’un bout de papier et d’un stylo non ça c’est moi qui me paluche ce travail
  • D’un bouton pour activer ou désactiver le mode aléatoire (et une variable pour stocker cet état, évidemment)
  • D’une belle icône pour ce bouton
  • D’une fonction qui sera activée lorsqu’on clique sur le bouton (et qui donc servira à activer ou désactiver le mode aléatoire, un peu comme la fonction Mute)
  • Un générateur de nombre aléatoire, ce qui peut servir pour faire une fonction de lecture… aléatoire !

J’ai fait un petit sondage sur Twitter pour savoir si un morceau de la playlist pouvait être répété (ce qui se produit avec VLC par exemple), ou s’il devait absolument ne pas être rejoué : la première solution l’emporte haut la main avec un résultat digne d’une élection présidentielle de 2002 en France, avec 86% des voix (sur 14 votes exprimés) ! Cela veut dire dire que je dois noter quelque part (une liste, au hasard) que tel morceau a été joué, afin que si mon générateur de nombre aléatoire « retombe » sur le même morceau, on regénère un autre nombre aléatoire (jusqu’à trouver un morceau non-joué : c’est facile au départ, mais sur une playlist courte, ça va vite de se retrouver à court de morceaux non-joués !).

  • Donc, une liste pour stocker l’état de chaque morceau (non-joué/joué)
  • Et quelques autres bricoles of course

Kiki parti c’est mon

Tout d’abord, nous allons créer une petite variable globale pour stocker l’état de notre mode de lecture (j’ai mis ça dans la région Variables) :

$global:shuffle=0 # 0: pas aléatoire, lecture en séquence, 1: aléatoire

J’aurai très bien pu utiliser un booléen ($true ou $false) mais j’aime bien les valeurs numériques moi. Au passage, un booléen ne convient pas dès lors qu’on veut un état supplémentaire (à partir d’un troisième), et bien souvent il arrive qu’on fasse évoluer un programme… et si vous devez refaire tous vos « if… then » parce qu’une variable est passée d’un type booléen à entier, ben c’est vite chiant. Certes, dans le cas présent, je vois mal ce qu’on pourrait mettre comme troisième état mais qui sait… Enfin, faites ce que vous voulez, mais moi j’utilise des entiers pour ce genre de truc. Ayatollahs du boolisme, j’attends vos fatwas.

Ensuite, nous allons créer notre fameuse liste pour stocker l’état joué/non-joué de nos morceaux. Comme cette liste aura la même taille que $playlist, ne nous gênons pas pour la créer de manière parfaitement identique, dans la région Création des objets :

$shufflelist= New-Object System.Collections.ArrayList

Cette $shufflelist devra être initialisée dans la fonction scandir, au même moment que l’on crée la playlist temporaire (celle qui n’est pas triée). Ainsi, dans la boucle for…each de la fonction scandir, on trouvera ces deux lignes :

$playlisttmp.Add($item) | Out-Null
$shufflelist.Add(0) | Out-Null

Et oui, en gros, on la remplit de 0 à chaque fois qu’on rajoute un item (objet « morceau ») à la playlist temporaire, vu qu’à ce stade aucun morceau n’a été rejoué. Et évidemment, à la lecture de tel morceau, il faudra penser à mettre $shufflelist à jour, en mettant la valeur à 1, pour le morceau d’index $global:i.

On a initialisé ce dont on a besoin, on va vite fait se créer un bouton qui va bien.

Bien un va bouton qui

Comme je le disais plus haut, plutôt qu’un bouton, c’est en réalité une checkbox ayant l’apparence d’un bouton qui va nous servir :

# bouton (enfin, checkbox plutôt) Shuffle
$checkbox2 = New-Object System.Windows.Forms.CheckBox
$checkbox2.Size = New-Object System.Drawing.Size(30,30)
$checkbox2.Location = New-Object System.Drawing.Size(330,370)
$checkbox2.Appearance="button"
$checkbox2.Image=[System.Drawing.Image]::FromFile("$path\shuffle.png")
$checkbox2.Checked=$true
$checkbox2.Name="shuffle"
$checkbox2.add_MouseHover($help)
$form.Controls.Add($checkbox2)

$checkbox2.add_CheckedChanged({setshuffle})

C’est un gros copié/coller de $checkbox1, outre le nom, la position sur le formulaire, l’image et le nom de la fonction appelée (setshuffle), c’est à peu près la copie conforme de notre bouton Mute.

Pensez à mettre à jour notre petit tooltip dans la région Variables en ajoutant une ligne concernant notre bouton appelé « shuffle » :

$help={    
    Switch ($this.name) 
    {
        "play"  {$tip = "Lecture/Pause"}
        "stop" {$tip = "Stop"}
        "next" {$tip = "Piste suivante"}
        "prev" {$tip = "Piste précédente"}
        "mute" {$tip = "Couper/Rallumer le son"}
        "browse" {$tip = "Sélectionner le dossier"}
        "shuffle" {$tip = "Lecture aléatoire"}
        "info" {$tip = "A propos de DédéAmp Player v$version"}
    }
    $tooltip.SetToolTip($this,$tip)
}

La setshuffle fonction

On ne peut guère faire plus simple. Si le bouton (qui est une checkbox) est sélectionné, alors on passe en lecture aléatoire. Et inversement. C’est la fonction Mute mais en encore plus simple, car on n’a pas besoin de changer l’image du bouton :

function setshuffle
{
    if($checkbox2.Checked -eq $true)
    {
        $global:shuffle=1
    }
    else
    {
        $global:shuffle=0
    }
}

Voilà, tout est posé, reste plus qu’à réfléchir un chouilla. Où mettre le code qui va choisir un morceau de manière aléatoire ? En fait, c’est assez simple : autant caser tout ça dans la fonction play, et comme ça hop, même pas besoin de modifier les fonctions next et prev ! Du boulot en moins dites donc, moi ça me plaît, pas vous ?

Play revisited fonction la

function play
{
    # shuffle
    if($global:shuffle -eq 1)
    {
        $endloop=0
        do 
        {
            if($trackcounter -eq $global:nbfiles)
            {
                # On réinitialise la shufflelist
                $shufflelist.Clear()
                $trackcounter=0
                for($k=0; $k -le $global:nbfiles;$k++)
                {
                    $shufflelist.Add(0)
                }
            }
           
            $global:i=Get-Random -Maximum $global:nbfiles
            $trackcounter++
            if($shufflelist[$global:i] -eq 0)
            {
                $shufflelist[$global:i]=1
                $endloop=1
            }
            # write-host "Randomly selected track : $global:i"
        }
        until ($endloop -eq 1)
    }

Voilà tout ce qu’on ajoute AU DÉBUT de la fonction play. Le reste on n’y touche surtout pas.  Évidemment, nous allons décortiquer un petit peu tout ça.

Ce bout de code ne sera exécuté que si la variable $global:shuffle est égale à 1. Dans le cas contraire, on s’en fiche totalement, le programme est déjà prévu pour fonctionner sans lecture aléatoire, il n’y a rien à changer. Je crée la variable $endloop qui va me servir de flag (drapeau) pour indiquer si je peux sortir de la boucle do…until. Alors déjà, c’est quoi ça ?

Comme son nom l’indique, une boucle do…until exécute le bloc de code qu’elle contient jusqu’à (until) ce qu’une condition soit remplie (et ici, la condition c’est que $endloop soit égale à 1). N’oubliez pas que les twittas et twittos qui me font l’honneur de me suivre ont voté majoritairement pour qu’un morceau ne soit pas rejoué. Donc si on tombe sur un morceau déjà joué, il faut en choisir un autre ! Ah pas de bol, celui-là aussi a déjà été joué, bon on en cherche encore un autre… etc, etc.

Attention avec les boucles do…until : si la condition de sortie n’est jamais remplie, à vous les joies des boucles infinies… et là, pas le choix, il faut tuer Powershell, parce qu’il ne vous rendra jamais la main… Et cela risque de se produire dès qu’on aura joué TOUTE la playlist (ce qui arrivera d’autant plus vite que celle-ci est petite) : plus aucun morceau à jouer, mais DédéAmp continuera d’en chercher un… qu’il ne trouvera jamais ! D’où mon petit compteur $trackcounter : celui-ci sera incrémenté à chaque fois qu’on sera tombé sur un morceau jouable. Et dès qu’il attendra le nombre total de morceaux $global:nbfiles, on va réinitialiser $shufflelist (en la vidant avec la méthode Clear(), puis en la re-remplissant de zéros). Et le tour est joué, on trouvera TOUJOURS un morceau à rejouer ! Et à ce moment là, on mettra $endloop à 1 histoire de sortir de notre boucle do…until infernale. Le reste de la fonction play se chargera de jouer ledit morceau, sans même à avoir changer quoi que ce soit dans le reste de la fonction (comme quoi c’était bien fait dès le début, wow, pour une fois que ça m’arrive…).

Vous vous rappelez que la variable $global:i contient le numéro d’index dans la playlist (définitive) à jouer ? Et bien c’est cette variable que l’on va forcer à prendre une valeur aléatoire, grâce à la cmdlet Get-Random :

$global:i=Get-Random -Maximum $global:nbfiles

J’indique un maximum (le nombre total – donc maximal – de morceaux dans la playlist). J’aurai pu indiquer un minimum à 0 (avec le paramètre -Minimum) mais cela n’a aucun intérêt dans notre cas, puisque par défaut, ce minimum est de 0.

Si vous voulez voir que la lecture se fait bien en aléatoire, vous pouvez au choix :

  • Décommenter cette ligne, qui affichera le numéro de la piste joué :
# write-host "Randomly selected track : $global:i"
  • Regarder dans le formulaire de DédéAmp que le numéro dans la playlist n’est pas séquentiel

Fini c’est voilà !

C’est sur ce jeu de mot téléphoné reprenant le titre d’une célèbre chanson que je vous laisse en attendant un prochain article. D’ici là, portez-vous bien et tchüss !

PS : de sombres histoires de proxy du boulot m’empêchant d’uploader un fichier en FTP m’obligent à reporter à ce soir la mise à disposition de l’archive contenant cette nouvelle version de DédéAmp. Alors patchez-le vous même ou attendez ce soir !

 

 

 

 

 

 

 

Montre ton amour pour Dédé en partageant cet article !

Laisser un commentaire